xref: /illumos-gate/usr/src/lib/libnisdb/ldap_map.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 #include <strings.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <rpcsvc/nis.h>
32 #include <rpc/xdr.h>
33 
34 #include "ldap_util.h"
35 #include "ldap_attr.h"
36 #include "ldap_ruleval.h"
37 #include "ldap_op.h"
38 #include "ldap_map.h"
39 #include "ldap_glob.h"
40 #include "ldap_xdr.h"
41 #include "ldap_val.h"
42 
43 /* From yptol/dit_access_utils.h */
44 #define	N2LKEY		"rf_key"
45 #define	N2LIPKEY	"rf_ipkey"
46 
47 __nis_hash_table_mt	ldapMappingList = NIS_HASH_TABLE_MT_INIT;
48 extern	int yp2ldap;
49 
50 
51 int
52 setColumnNames(__nis_table_mapping_t *t) {
53 	int	i, j, nic, noc;
54 	char	**col;
55 	zotypes	type;
56 	char	*myself = "setColumnNames";
57 
58 	if (t == 0)
59 		return (0);
60 
61 	type = t->objType;
62 	col = t->column;
63 	nic = (col != 0) ? t->numColumns : -1;
64 
65 	t->objType = NIS_BOGUS_OBJ;
66 	t->obj = 0;
67 
68 	/*
69 	 * If it's a table object, but there are no translation rules,
70 	 * this mapping is for the table object itself. In that case,
71 	 * we throw away the column names (if any).
72 	 */
73 	if (t->objType == NIS_TABLE_OBJ && t->numRulesFromLDAP == 0 &&
74 			t->numRulesToLDAP == 0) {
75 		for (i = 0; i < t->numColumns; i++)
76 			sfree(t->column[i]);
77 		sfree(t->column);
78 		t->column = 0;
79 		t->numColumns = 0;
80 		noc = 0;
81 	}
82 
83 	/*
84 	 * Verify that all column names found by the parser
85 	 * are present in the actual column list.
86 	 */
87 	if (verbose) {
88 		for (i = 0, noc = 0; i < nic; i++) {
89 			int	found = 0;
90 
91 			if (col[i] == 0)
92 				continue;
93 			/* Skip the 'zo_*' special column names */
94 			if (isObjAttrString(col[i]))
95 				continue;
96 			for (j = 0; j < t->numColumns; j++) {
97 				if (strcmp(col[i], t->column[j]) == 0) {
98 					noc++;
99 					found = 1;
100 					break;
101 				}
102 			}
103 			if (!found) {
104 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
105 					"%s: No column \"%s\" in \"%s\"",
106 					myself, NIL(col[i]), NIL(t->objName));
107 			}
108 		}
109 	}
110 
111 	/* Remove any setup by the parser */
112 	for (i = 0; i < nic; i++) {
113 		sfree(col[i]);
114 	}
115 	sfree(col);
116 
117 	return (0);
118 }
119 
120 void
121 freeSingleObjAttr(__nis_obj_attr_t *attr) {
122 	if (attr == 0)
123 		return;
124 
125 	sfree(attr->zo_owner);
126 	sfree(attr->zo_group);
127 	sfree(attr->zo_domain);
128 	sfree(attr);
129 }
130 
131 void
132 freeObjAttr(__nis_obj_attr_t **attr, int numAttr) {
133 	int	i;
134 
135 	if (attr == 0)
136 		return;
137 
138 	for (i = 0; i < numAttr; i++) {
139 		freeSingleObjAttr(attr[i]);
140 	}
141 
142 	sfree(attr);
143 }
144 
145 __nis_obj_attr_t *
146 cloneObjAttr(__nis_obj_attr_t *old) {
147 	__nis_obj_attr_t	*new;
148 	char			*myself = "cloneObjAttr";
149 
150 	if (old == 0)
151 		return (0);
152 
153 	new = am(myself, sizeof (*new));
154 	if (new == 0)
155 		return (0);
156 
157 	new->zo_owner = sdup(myself, T, old->zo_owner);
158 	if (new->zo_owner == 0 && old->zo_owner != 0)
159 		goto cleanup;
160 
161 	new->zo_group = sdup(myself, T, old->zo_group);
162 	if (new->zo_group == 0 && old->zo_group != 0)
163 		goto cleanup;
164 
165 	new->zo_domain = sdup(myself, T, old->zo_domain);
166 	if (new->zo_domain == 0 && old->zo_domain != 0)
167 		goto cleanup;
168 
169 	new->zo_access = old->zo_access;
170 	new->zo_ttl = old->zo_ttl;
171 
172 	return (new);
173 
174 cleanup:
175 	freeSingleObjAttr(new);
176 
177 	return (0);
178 }
179 
180 
181 /*
182  * Obtain NIS+ entries (in the form of db_query's) from the supplied table
183  * mapping and db_query.
184  *
185  * If 'qin' is NULL, enumeration is desired.
186  *
187  * On exit, '*numQueries' contains the number of (db_query *)'s in the
188  * return array, '*ldapStat' the LDAP operation status, and '*objAttr'
189  * a pointer to an array (of '*numQueries elements) of object attributes
190  * (zo_owner, etc.). If no object attributes were retrieved, '*objAttr'
191  * is NULL; any and all of the (*objAttr)[i]'s may be NULL.
192  */
193 db_query **
194 mapFromLDAP(__nis_table_mapping_t *t, db_query *qin, int *numQueries,
195 		char *dbId, int *ldapStat, __nis_obj_attr_t ***objAttr) {
196 	__nis_table_mapping_t	**tp;
197 	db_query		**q;
198 	__nis_rule_value_t	*rv;
199 	__nis_ldap_search_t	*ls;
200 	int			n, numVals, numMatches = 0;
201 	int			stat;
202 	__nis_obj_attr_t	**attr;
203 	char			*myself = "mapFromLDAP";
204 
205 	if (ldapStat == 0)
206 		ldapStat = &stat;
207 
208 	if (t == 0 || numQueries == 0) {
209 		*ldapStat = LDAP_PARAM_ERROR;
210 		return (0);
211 	}
212 
213 	/* Select the correct table mapping(s) */
214 	tp = selectTableMapping(t, qin, 0, 0, dbId, &numMatches);
215 	if (tp == 0 || numMatches <= 0) {
216 		/*
217 		 * Not really an error; just no matching mapping
218 		 * for the query.
219 		 */
220 		*ldapStat = LDAP_SUCCESS;
221 		return (0);
222 	}
223 
224 	q = 0;
225 	attr = 0;
226 
227 	/* For each mapping */
228 	for (numVals = 0, n = 0; n < numMatches; n++) {
229 		db_query		**qt;
230 		int			i, nqt = 0, filterOnQin, res = 0;
231 
232 		t = tp[n];
233 
234 		if (qin != 0) {
235 			rv = buildNisPlusRuleValue(t, qin, 0);
236 			if (rv != 0) {
237 				/*
238 				 * Depending on the value of res, we shall
239 				 * proceed to next table mapping.
240 				 */
241 				ls = createLdapRequest(t, rv, 0, 1, &res, NULL);
242 			}
243 			else
244 				ls = 0;
245 		} else {
246 			/* Build enumeration request */
247 			rv = 0;
248 			ls = createLdapRequest(t, 0, 0, 1, NULL, NULL);
249 		}
250 
251 		freeRuleValue(rv, 1);
252 
253 		if (ls == 0) {
254 			/*
255 			 * if the res is NP_LDAP_RULES_NO_VALUE, that means we
256 			 * have enough NIS+ columns for the rules to produce
257 			 * values, but none of them did, so continue to the
258 			 * next table mapping. Otherwise do cleanup and return
259 			 * error.
260 			 */
261 			if (res == NP_LDAP_RULES_NO_VALUE)
262 				continue;
263 			for (i = 0; i < numVals; i++)
264 				freeQuery(q[i]);
265 			sfree(q);
266 			free(tp);
267 			*ldapStat = LDAP_OPERATIONS_ERROR;
268 			return (0);
269 		}
270 
271 		/* Query LDAP */
272 		nqt = (ls->isDN || qin != 0) ? 0 : -1;
273 		rv = ldapSearch(ls, &nqt, 0, ldapStat);
274 
275 		/*
276 		 * If qin != 0, then we need to make sure that the
277 		 * LDAP search is filtered so that only entries that
278 		 * are compatible with 'qin' are retained. This will
279 		 * happen automatically if we do a DN search (in which
280 		 * case, no need to filter on 'qin').
281 		 */
282 		if (ls->isDN || qin == 0)
283 			filterOnQin = 0;
284 		else
285 			filterOnQin = 1;
286 
287 		freeLdapSearch(ls);
288 
289 		/* Convert rule-values to db_query's */
290 		if (rv != 0 && nqt > 0) {
291 			int			nrv = nqt;
292 			__nis_obj_attr_t	**at = 0;
293 
294 			qt = ruleValue2Query(t, rv,
295 				(filterOnQin) ? qin : 0, &at, &nqt);
296 			freeRuleValue(rv, nrv);
297 
298 			if (qt != 0 && q == 0) {
299 				q = qt;
300 				attr = at;
301 				numVals = nqt;
302 			} else if (qt != 0) {
303 				db_query		**tmp;
304 				__nis_obj_attr_t	**atmp;
305 
306 				/* Extend the 'q' array */
307 				tmp = realloc(q,
308 					(numVals+nqt) * sizeof (q[0]));
309 				/* ... and the 'attr' array */
310 				atmp = realloc(attr,
311 					(numVals+nqt) * sizeof (attr[0]));
312 				if (tmp == 0 || atmp == 0) {
313 					logmsg(MSG_NOMEM, LOG_ERR,
314 						"%s: realloc(%d) => NULL",
315 						myself,
316 						(numVals+nqt) * sizeof (q[0]));
317 					for (i = 0; i < numVals; i++)
318 						freeQuery(q[i]);
319 					for (i = 0; i < nqt; i++)
320 						freeQuery(qt[i]);
321 					sfree(tmp);
322 					sfree(atmp);
323 					sfree(q);
324 					sfree(qt);
325 					sfree(tp);
326 					freeObjAttr(at, nqt);
327 					freeObjAttr(attr, numVals);
328 					*ldapStat = LDAP_NO_MEMORY;
329 					return (0);
330 				}
331 				q = tmp;
332 				attr = atmp;
333 				/* Add the results for this 't' */
334 				(void) memcpy(&q[numVals], qt,
335 						nqt * sizeof (qt[0]));
336 				(void) memcpy(&attr[numVals], at,
337 						nqt * sizeof (at[0]));
338 				numVals += nqt;
339 
340 				sfree(qt);
341 				sfree(at);
342 			}
343 		}
344 	}
345 
346 	*numQueries = numVals;
347 	if (objAttr != 0)
348 		*objAttr = attr;
349 	else
350 		freeObjAttr(attr, numVals);
351 	sfree(tp);
352 
353 	return (q);
354 }
355 
356 /*
357  * Add the object attributes (zo_owner, etc.) to the rule-value 'rv'.
358  * Returns a pointer to the (possibly newly allocated) rule-value,
359  * or NULL in case of failure. If not returning 'rvIn', the latter
360  * will have been freed.
361  */
362 __nis_rule_value_t *
363 addObjAttr2RuleValue(nis_object *obj, __nis_rule_value_t *rvIn) {
364 	__nis_rule_value_t	*rv;
365 	char			abuf[2 * sizeof (obj->zo_access) + 1];
366 	char			tbuf[2 * sizeof (obj->zo_ttl) + 1];
367 
368 	if (obj == 0)
369 		return (0);
370 
371 	if (rvIn != 0) {
372 		rv = rvIn;
373 	} else {
374 		rv = initRuleValue(1, 0);
375 		if (rv == 0)
376 			return (0);
377 	}
378 
379 	if (obj->zo_owner != 0) {
380 		if (addSCol2RuleValue("zo_owner", obj->zo_owner, rv) != 0) {
381 			freeRuleValue(rv, 1);
382 			return (0);
383 		}
384 	}
385 
386 	if (obj->zo_group != 0) {
387 		if (addSCol2RuleValue("zo_group", obj->zo_group, rv) != 0) {
388 			freeRuleValue(rv, 1);
389 			return (0);
390 		}
391 	}
392 
393 	if (obj->zo_domain != 0) {
394 		if (addSCol2RuleValue("zo_domain", obj->zo_domain, rv) != 0) {
395 			freeRuleValue(rv, 1);
396 			return (0);
397 		}
398 	}
399 
400 	(void) memset(abuf, 0, sizeof (abuf));
401 	(void) memset(tbuf, 0, sizeof (tbuf));
402 
403 	sprintf(abuf, "%x", obj->zo_access);
404 	sprintf(tbuf, "%x", obj->zo_ttl);
405 
406 	if (addSCol2RuleValue("zo_access", abuf, rv) != 0) {
407 		freeRuleValue(rv, 1);
408 		return (0);
409 	}
410 	if (addSCol2RuleValue("zo_ttl", tbuf, rv) != 0) {
411 		freeRuleValue(rv, 1);
412 		return (0);
413 	}
414 
415 	return (rv);
416 }
417 
418 /*
419  * Returns a pointer to (NOT a copy of) the value for the specified
420  * column 'col' in the rule-value 'rv'.
421  */
422 __nis_value_t *
423 findColValue(char *col, __nis_rule_value_t *rv) {
424 	int		i;
425 
426 	if (col == 0 || rv == 0 || rv->numColumns <= 0)
427 		return (0);
428 
429 	for (i = 0; i < rv->numColumns; i++) {
430 		if (strcmp(col, rv->colName[i]) == 0)
431 			return (&rv->colVal[i]);
432 	}
433 
434 	return (0);
435 }
436 
437 /*
438  * Return the NIS+ object attributes (if any) in the rule-value 'rv'.
439  */
440 __nis_obj_attr_t *
441 ruleValue2ObjAttr(__nis_rule_value_t *rv) {
442 	__nis_obj_attr_t	*attr;
443 	__nis_value_t		*val;
444 	char			*myself = "ruleValue2ObjAttr";
445 
446 	if (rv == 0 || rv->numColumns <= 0)
447 		return (0);
448 
449 	attr = am(myself, sizeof (*attr));
450 
451 	if ((val = findColValue("zo_owner", rv)) != 0 &&
452 			val->type == vt_string && val->numVals == 1 &&
453 			val->val[0].value != 0) {
454 		attr->zo_owner = sdup(myself, T, val->val[0].value);
455 		if (attr->zo_owner == 0) {
456 			freeSingleObjAttr(attr);
457 			return (0);
458 		}
459 	}
460 
461 	if ((val = findColValue("zo_group", rv)) != 0 &&
462 			val->type == vt_string && val->numVals == 1 &&
463 			val->val[0].value != 0) {
464 		attr->zo_group = sdup(myself, T, val->val[0].value);
465 		if (attr->zo_group == 0) {
466 			freeSingleObjAttr(attr);
467 			return (0);
468 		}
469 	}
470 
471 	if ((val = findColValue("zo_domain", rv)) != 0 &&
472 			val->type == vt_string && val->numVals == 1 &&
473 			val->val[0].value != 0) {
474 		attr->zo_domain = sdup(myself, T, val->val[0].value);
475 		if (attr->zo_domain == 0) {
476 			freeSingleObjAttr(attr);
477 			return (0);
478 		}
479 	}
480 
481 	if ((val = findColValue("zo_access", rv)) != 0 &&
482 			val->type == vt_string && val->numVals == 1 &&
483 			val->val[0].value != 0) {
484 		if (sscanf(val->val[0].value, "%x", &attr->zo_access) != 1) {
485 			freeSingleObjAttr(attr);
486 			return (0);
487 		}
488 	}
489 
490 	if ((val = findColValue("zo_ttl", rv)) != 0 &&
491 			val->type == vt_string && val->numVals == 1 &&
492 			val->val[0].value != 0) {
493 		if (sscanf(val->val[0].value, "%x", &attr->zo_ttl) != 1) {
494 			freeSingleObjAttr(attr);
495 			return (0);
496 		}
497 	}
498 
499 	return (attr);
500 }
501 
502 /*
503  * If the supplied string is one of the object attributes, return one.
504  * Otherwise, return zero.
505  */
506 int
507 isObjAttrString(char *str) {
508 	if (str == 0)
509 		return (0);
510 
511 	if (strcmp("zo_owner", str) == 0 ||
512 		strcmp("zo_group", str) == 0 ||
513 		strcmp("zo_domain", str) == 0 ||
514 		strcmp("zo_access", str) == 0 ||
515 		strcmp("zo_ttl", str) == 0)
516 		return (1);
517 	else
518 		return (0);
519 }
520 
521 
522 /*
523  * If the supplied value is one of the object attribute strings, return
524  * a pointer to the string. Otherwise, return NULL.
525  */
526 char *
527 isObjAttr(__nis_single_value_t *val) {
528 	if (val == 0 || val->length <= 0 || val->value == 0)
529 		return (0);
530 
531 	if (isObjAttrString(val->value))
532 		return (val->value);
533 	else
534 		return (0);
535 }
536 
537 int
538 setObjAttrField(char *attrName, __nis_single_value_t *val,
539 		__nis_obj_attr_t **objAttr) {
540 	__nis_obj_attr_t	*attr;
541 	char			*myself = "setObjAttrField";
542 
543 	if (attrName == 0 || val == 0 || objAttr == 0 ||
544 			val->value == 0 || val->length <= 0)
545 		return (-1);
546 
547 	if (*objAttr != 0) {
548 		attr = *objAttr;
549 	} else {
550 		attr = am(myself, sizeof (*attr));
551 		if (attr == 0)
552 			return (-2);
553 		*objAttr = attr;
554 	}
555 
556 	if (strcmp("zo_owner", attrName) == 0) {
557 		if (attr->zo_owner == 0) {
558 			attr->zo_owner = sdup(myself, T, val->value);
559 			if (attr->zo_owner == 0)
560 				return (-11);
561 		}
562 	} else if (strcmp("zo_group", attrName) == 0) {
563 		if (attr->zo_group == 0) {
564 			attr->zo_group = sdup(myself, T, val->value);
565 			if (attr->zo_group == 0)
566 				return (-12);
567 		}
568 	} else if (strcmp("zo_domain", attrName) == 0) {
569 		if (attr->zo_domain == 0) {
570 			attr->zo_domain = sdup(myself, T, val->value);
571 			if (attr->zo_domain == 0)
572 				return (-13);
573 		}
574 	} else if (strcmp("zo_access", attrName) == 0) {
575 		if (attr->zo_access == 0) {
576 			if (sscanf(val->value, "%x", &attr->zo_access) != 1)
577 				return (-14);
578 		}
579 	} else if (strcmp("zo_ttl", attrName) == 0) {
580 		if (attr->zo_ttl == 0) {
581 			if (sscanf(val->value, "%x", &attr->zo_ttl) != 1)
582 				return (-15);
583 		}
584 	}
585 
586 	return (0);
587 }
588 
589 /*
590  * Return a DN and rule-value for the supplied mapping, db_query's, and
591  * input rule-value. This function only works on a single mapping. See
592  * mapToLDAP() below for a description of the action depending on the
593  * values of 'old' and 'new'.
594  *
595  * If both 'old' and 'new' are supplied, and the modify would result
596  * in a change to the DN, '*oldDN' will contain the old DN. Otherwise
597  * (and normally), '*oldDN' will be NULL.
598  */
599 char *
600 map1qToLDAP(__nis_table_mapping_t *t, db_query *old, db_query *new,
601 		__nis_rule_value_t *rvIn, __nis_rule_value_t **rvOutP,
602 		char **oldDnP) {
603 
604 	__nis_rule_value_t	*rv, *rvt;
605 	__nis_ldap_search_t	*ls;
606 	char			*dn = 0, *oldDn = 0;
607 	__nis_table_mapping_t	del;
608 	char			*myself = "map1qToLDAP";
609 
610 	if (t == 0 || (old == 0 && new == 0) || rvOutP == 0)
611 		return (0);
612 
613 	/*
614 	 * If entry should be deleted, we look at the delete
615 	 * policy in the table mapping. Should it specify a
616 	 * rule set, we use that rule set to build a rule-
617 	 * value, and the delete actually becomes a modify
618 	 * operation.
619 	 */
620 	if (old != 0 && new == 0) {
621 		if (t->objectDN->delDisp == dd_perDbId) {
622 			/*
623 			 * The functions that build a rule-value from a
624 			 * rule set expect a __nis_table_mapping_t, but the
625 			 * rule set in the __nis_object_dn_t isn't of that
626 			 * form. So, build a pseudo-__nis_table_mapping_t that
627 			 * borrows heavily from 't'.
628 			 */
629 			del = *t;
630 
631 			del.numRulesToLDAP = del.objectDN->numDbIds;
632 			del.ruleToLDAP = del.objectDN->dbId;
633 
634 			/*
635 			 * Do a modify with the pseudo-table
636 			 * mapping, and the 'old' db_query
637 			 * supplying input to the delete rule
638 			 * set.
639 			 */
640 			t = &del;
641 			new = old;
642 		} else if (t->objectDN->delDisp == dd_always) {
643 
644 			/* Nothing to do here; all handled below */
645 
646 		} else if (t->objectDN->delDisp == dd_never) {
647 
648 			return (0);
649 
650 		} else {
651 
652 			logmsg(MSG_INVALIDDELDISP, LOG_WARNING,
653 				"%s: Invalid delete disposition %d for \"%s\"",
654 				myself, t->objectDN->delDisp,
655 				NIL(t->dbId));
656 			return (0);
657 
658 		}
659 	}
660 
661 	/* Make a copy of the input rule-value */
662 	if (rvIn != 0) {
663 		rv = initRuleValue(1, rvIn);
664 		if (rv == 0)
665 			return (0);
666 	} else {
667 		rv = 0;
668 	}
669 
670 	/* First get a rule-value from the supplied NIS+ entry. */
671 	rvt = rv;
672 	rv = buildNisPlusRuleValue(t, ((old != 0) ? old : new), rvt);
673 	freeRuleValue(rvt, 1);
674 	if (rv == 0) {
675 		logmsg(MSG_NORULEVALUE, LOG_WARNING,
676 			"%s: No in-query rule-value derived for \"%s\"",
677 			myself, NIL(t->dbId));
678 		return (0);
679 	}
680 
681 	/*
682 	 * Create a request (really only care about the DN) from the
683 	 * supplied NIS+ entry data.
684 	 */
685 	ls = createLdapRequest(t, rv, &dn, 0, NULL, NULL);
686 	if (ls == 0 || dn == 0) {
687 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
688 			"%s: Unable to create LDAP request for %s: %s",
689 			myself, NIL(t->dbId),
690 			(dn != 0) ? dn : rvId(rv, mit_nisplus));
691 		sfree(dn);
692 		freeLdapSearch(ls);
693 		freeRuleValue(rv, 1);
694 		return (0);
695 	}
696 
697 	freeLdapSearch(ls);
698 
699 	if (new != 0) {
700 		/*
701 		 * Create a rule-value from the new NIS+ entry.
702 		 * Don't want to mix in the rule-value derived
703 		 * from 'old', so delete it. However, we still
704 		 * want the owner, group, etc., from 'rvIn'.
705 		 */
706 		if (old != 0) {
707 			freeRuleValue(rv, 1);
708 			if (rvIn != 0) {
709 				rv = initRuleValue(1, rvIn);
710 				if (rv == 0) {
711 					sfree(dn);
712 					return (0);
713 				}
714 			} else {
715 				rv = 0;
716 			}
717 		}
718 		rvt = rv;
719 		rv = buildNisPlusRuleValue(t, new, rvt);
720 		freeRuleValue(rvt, 1);
721 		if (rv == 0) {
722 			logmsg(MSG_NORULEVALUE, LOG_WARNING,
723 				"%s: No new rule-value derived for \"%s: %s\"",
724 				myself, NIL(t->dbId), dn);
725 			sfree(dn);
726 			return (0);
727 		}
728 		/*
729 		 * Check if the proposed modification would result in a
730 		 * a change to the DN.
731 		 */
732 		if (old != 0) {
733 			oldDn = dn;
734 			dn = 0;
735 			ls = createLdapRequest(t, rv, &dn, 0, NULL, NULL);
736 			if (ls == 0 || dn == 0) {
737 				logmsg(MSG_NOTIMECHECK, LOG_ERR,
738 				"%s: Unable to create new DN for \"%s: %s\"",
739 					myself, NIL(t->dbId), oldDn);
740 				sfree(oldDn);
741 				freeLdapSearch(ls);
742 				freeRuleValue(rv, 1);
743 				return (0);
744 			}
745 			freeLdapSearch(ls);
746 			if (strcasecmp(oldDn, dn) == 0) {
747 				sfree(oldDn);
748 				oldDn = 0;
749 			}
750 		}
751 	}
752 
753 
754 	*rvOutP = rv;
755 	if (oldDnP != 0)
756 		*oldDnP = oldDn;
757 
758 	return (dn);
759 }
760 
761 /*
762  * Since the DN hash list is an automatic variable, there's no need for
763  * locking, and we remove the locking overhead by using the libnsl
764  * hash functions.
765  */
766 #undef  NIS_HASH_ITEM
767 #undef  NIS_HASH_TABLE
768 
769 typedef struct {
770 	NIS_HASH_ITEM	item;
771 	int		index;
772 	char		*oldDn;
773 } __dn_item_t;
774 
775 /*
776  * Update LDAP per the supplied table mapping and db_query's.
777  *
778  * 'nq' is the number of elements in the 'old', 'new', and 'rvIn'
779  * arrays. mapToLDAP() generally performs one update for each
780  * element; however, if one or more of the individual queries
781  * produce the same DN, they're merged into a single update.
782  *
783  * There are four cases, depending on the values of 'old[iq]' and
784  * 'new[iq]':
785  *
786  * (1)	old[iq] == 0 && new[iq] == 0
787  *	No action; skip to next query
788  *
789  * (2)	old[iq] == 0 && new[iq] != 0
790  *	Attempt to use the 'new' db_query to get a DN, and try to create
791  *	the corresponding LDAP entry.
792  *
793  * (3)	old[iq] != 0 && new[iq] == 0
794  *	Use the 'old' db_query to get a DN, and try to delete the LDAP
795  *	entry per the table mapping.
796  *
797  * (4)	old[iq] != 0 && new[iq] != 0
798  *	Use the 'old' db_query to get a DN, and update (possibly create)
799  *	the corresponding LDAP entry per the 'new' db_query.
800  *
801  * If 'rvIn' is non-NULL, it is expected to contain the object attributes
802  * (zo_owner, etc.) to be written to LDAP. 'rvIn' is an array with 'nq'
803  * elements.
804  *
805  * If 'firstOnly' is set, only the first old[iq]/new[iq] pair is used
806  * to perform the actual update. Any additional queries specified will
807  * have their values folded in, but are not used to derive update targets.
808  * This mode is inteded to support the case where multiple NIS+ entries
809  * map to one and the same LDAP entry. Note that 'rvIn' must still be
810  * an array of 'nq' elements, though if 'firstOnly' is set, it should be
811  * OK to leave all but 'rvIn[0]' empty.
812  *
813  * 'dbId' is used to further narow down the selection of mapping candidates
814  * to those matching the 'dbId' value.
815  */
816 int
817 mapToLDAP(__nis_table_mapping_t *tm, int nq, db_query **old, db_query **new,
818 		__nis_rule_value_t *rvIn, int firstOnly, char *dbId) {
819 	__nis_table_mapping_t	**tp, **tpa;
820 	int			i, n, rnq, iq, r, ret = LDAP_SUCCESS;
821 	int			maxMatches, numMatches = 0;
822 	__nis_ldap_search_t	*ls;
823 	char			**dn = 0, **odn = 0;
824 	__nis_rule_value_t	**rv;
825 	__dn_item_t		*dni;
826 	char			*myself = "mapToLDAP";
827 
828 
829 	if (tm == 0 || (old == 0 && new == 0) || nq <= 0)
830 		return (LDAP_PARAM_ERROR);
831 
832 	/* Determine maximum number of table mapping matches */
833 	if (nq == 1) {
834 		tp = selectTableMapping(tm,
835 			(old != 0 && old[0] != 0) ? old[0] : new[0], 1, 0,
836 				dbId, &maxMatches);
837 		numMatches = maxMatches;
838 	} else {
839 		tp = selectTableMapping(tm, 0, 1, 0, dbId, &maxMatches);
840 	}
841 
842 	/*
843 	 * If no matching mapping, we're not mapping to LDAP in this
844 	 * particular case.
845 	 */
846 	if (tp == 0 || maxMatches == 0) {
847 		sfree(tp);
848 		return (LDAP_SUCCESS);
849 	}
850 
851 	/*
852 	 * Allocate the 'rv', 'dn', and 'tpa' arrays. Worst case is that
853 	 * we need nq * maxMatches elements in each array. However, if
854 	 * 'firstOnly' is set, we only need one element per matching
855 	 * mapping in each.
856 	 */
857 	dn = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (dn[0]));
858 	odn = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (odn[0]));
859 	rv = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (rv[0]));
860 	tpa = am(myself, (firstOnly ? 1 : nq) * maxMatches * sizeof (tpa[0]));
861 	if (dn == 0 || odn == 0 || rv == 0 || tpa == 0) {
862 		sfree(tp);
863 		sfree(dn);
864 		sfree(odn);
865 		sfree(rv);
866 		sfree(tpa);
867 		return (LDAP_NO_MEMORY);
868 	}
869 
870 	/* Unless nq == 1, we don't need the 'tp' value */
871 	if (nq != 1)
872 		sfree(tp);
873 
874 	logmsg(MSG_NOTIMECHECK,
875 #ifdef	NISDB_LDAP_DEBUG
876 		LOG_WARNING,
877 #else
878 		LOG_INFO,
879 #endif	/* NISDB_LDAP_DEBUG */
880 		"%s: %s: %d * %d potential updates",
881 		myself, NIL(tm->objName), nq, maxMatches);
882 
883 	/*
884 	 * Create DNs, column and attribute values, and merge duplicate DNs.
885 	 */
886 	for (iq = 0, rnq = 0; iq < nq; iq++) {
887 		int	idx;
888 
889 		if ((old == 0 || old[iq] == 0) &&
890 				(new == 0 || new[iq] == 0))
891 			continue;
892 
893 		/*
894 		 * Select matching table mappings; if nq == 1, we've already
895 		 * got the 'tp' array from above. We expect this to be the
896 		 * most common case, so it's worth special treatment.
897 		 */
898 		if (nq != 1)
899 			tp = selectTableMapping(tm,
900 			(old != 0 && old[iq] != 0) ? old[iq] : new[iq], 1, 0,
901 					dbId, &numMatches);
902 		if (tp == 0)
903 			continue;
904 		else if (numMatches <= 0) {
905 			sfree(tp);
906 			continue;
907 		}
908 
909 		idx = iq * maxMatches;
910 
911 		if (idx == 0 || !firstOnly)
912 			(void) memcpy(&tpa[idx], tp,
913 					numMatches * sizeof (tpa[idx]));
914 
915 		for (n = 0; n < numMatches; n++) {
916 			char			*dnt, *odnt;
917 			__nis_rule_value_t	*rvt = 0;
918 
919 			if (tp[n] == 0)
920 				continue;
921 
922 			dnt = map1qToLDAP(tp[n],
923 					(old != 0) ? old[iq] : 0,
924 					(new != 0) ? new[iq] : 0,
925 					(rvIn != 0) ? &rvIn[iq] : 0,
926 					&rvt, &odnt);
927 
928 			if (dnt == 0)
929 				continue;
930 			if (rvt == 0) {
931 #ifdef  NISDB_LDAP_DEBUG
932 				abort();
933 #else
934 				sfree(dnt);
935 				sfree(odnt);
936 				continue;
937 #endif	/* NISDB_LDAP_DEBUG */
938 			}
939 
940 			/*
941 			 * Create a request to get a rule-value with
942 			 * NIS+ data translated to LDAP equivalents.
943 			 */
944 			ls = createLdapRequest(tp[n], rvt, 0, 0, NULL, NULL);
945 			if (ls == 0) {
946 				if (ret == LDAP_SUCCESS)
947 					ret = LDAP_OPERATIONS_ERROR;
948 				logmsg(MSG_NOTIMECHECK, LOG_WARNING,
949 				"%s: Unable to map to LDAP attrs for %s:dn=%s",
950 				myself, NIL(tp[n]->dbId), dnt);
951 				sfree(dnt);
952 				freeRuleValue(rvt, 1);
953 				continue;
954 			}
955 			freeLdapSearch(ls);
956 
957 			/*
958 			 * If the DN is the same as one we already know
959 			 * about, merge the rule-values.
960 			 */
961 
962 			if ((iq == 0 || !firstOnly) && dnt != 0) {
963 				dni = am(myself, sizeof (*dni));
964 				if (dni != 0) {
965 					dni->item.name = dnt;
966 					dni->index = idx + n;
967 					dni->oldDn = odnt;
968 				} else {
969 					logmsg(MSG_NOTIMECHECK, LOG_WARNING,
970 					"%s: Skipping update for dn=\"%s\"",
971 						myself, dnt);
972 					sfree(dnt);
973 					dnt = 0;
974 				}
975 				if (dnt != 0) {
976 					dn[idx+n] = dnt;
977 					odn[idx+n] = odnt;
978 					rv[idx+n] = rvt;
979 					rnq++;
980 				} else {
981 					freeRuleValue(rvt, 1);
982 					rvt = 0;
983 				}
984 			} else if (dnt != 0) {
985 				sfree(dnt);
986 				sfree(odnt);
987 				freeRuleValue(rvt, 1);
988 			}
989 		}
990 		sfree(tp);
991 	}
992 
993 	logmsg(MSG_NOTIMECHECK,
994 #ifdef	NISDB_LDAP_DEBUG
995 		LOG_WARNING,
996 #else
997 		LOG_INFO,
998 #endif	/* NISDB_LDAP_DEBUG */
999 		"%s: %s: %d update%s requested",
1000 		myself, NIL(tm->objName), rnq, rnq != 1 ? "s" : "");
1001 
1002 	/* Perform the updates */
1003 	for (i = rnq = 0; i < (firstOnly ? maxMatches : nq*maxMatches); i++) {
1004 		int	delPerDbId;
1005 
1006 		if (dn[i] == 0)
1007 			continue;
1008 
1009 #ifdef	NISDB_LDAP_DEBUG
1010 		logmsg(MSG_NOTIMECHECK, LOG_INFO,
1011 			"%s: %s %s:dn=%s",
1012 			myself,
1013 			(new != 0 && new[i/maxMatches] != 0) ?
1014 				"modify" : "delete",
1015 			NIL(tpa[i]->dbId), dn[i]);
1016 #endif	/* NISDB_LDAP_DEBUG */
1017 
1018 		delPerDbId = (tpa[i]->objectDN->delDisp == dd_perDbId);
1019 		if ((new != 0 && new[i/maxMatches] != 0) || delPerDbId) {
1020 			/*
1021 			 * Try to modify/create the specified DN. First,
1022 			 * however, if the update changes the DN, make
1023 			 * that change.
1024 			 */
1025 			if (odn[i] == 0 || (r = ldapChangeDN(odn[i], dn[i])) ==
1026 					LDAP_SUCCESS) {
1027 				int	addFirst;
1028 
1029 				addFirst = (new != 0 &&
1030 						new[i/maxMatches] != 0 &&
1031 						!delPerDbId);
1032 				r = ldapModify(dn[i], rv[i],
1033 					tpa[i]->objectDN->write.attrs,
1034 						addFirst);
1035 			}
1036 		} else {
1037 			/* Try to delete the specified DN */
1038 			r = ldapModify(dn[i], 0,
1039 					tpa[i]->objectDN->write.attrs, 0);
1040 		}
1041 
1042 		if (r == LDAP_SUCCESS) {
1043 			rnq++;
1044 		} else {
1045 			if (ret == LDAP_SUCCESS)
1046 				ret = r;
1047 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
1048 				"%s: LDAP %s request error %d for %s:dn=%s",
1049 				myself,
1050 				(new != 0 && new[i/maxMatches] != 0) ?
1051 					"modify" : "delete",
1052 				r, NIL(tpa[i]->dbId), dn[i]);
1053 		}
1054 
1055 		sfree(dn[i]);
1056 		dn[i] = 0;
1057 		freeRuleValue(rv[i], 1);
1058 		rv[i] = 0;
1059 	}
1060 
1061 	sfree(dn);
1062 	sfree(odn);
1063 	sfree(rv);
1064 	sfree(tpa);
1065 
1066 	logmsg(MSG_NOTIMECHECK,
1067 #ifdef	NISDB_LDAP_DEBUG
1068 		LOG_WARNING,
1069 #else
1070 		LOG_INFO,
1071 #endif	/* NISDB_LDAP_DEBUG */
1072 		"%s: %s: %d update%s performed",
1073 		myself, NIL(tm->objName), rnq, rnq != 1 ? "s" : "");
1074 
1075 	return (ret);
1076 }
1077 
1078 /*
1079  * In nis+2ldap, check if the query 'q' matches the selector index 'x->index'.
1080  *
1081  * In nis2ldap, if 'name' is provided then check if its value in 'val'
1082  * matches the selector index. If 'name' is NULL, then check if rule-value 'rv'
1083  * matches the index.
1084  * To match the selector index, all fieldspecs in the indexlist should match
1085  * (AND). In nis2ldap, an exception is, if there are multiple fieldspecs with
1086  * the same fieldname then only one of them needs to match (OR).
1087  * Example:
1088  *	Indexlist = [host="H*", host="I*", user="U*", domain="D*"]
1089  * Then,
1090  *	host = "H1", user="U1", domain="D1" ==> pass
1091  *	host = "I1", user="U1", domain="D1" ==> pass
1092  *	host = "X1", user="U1", domain="D1" ==> fail
1093  *	host = "H1", user="X1", domain="D1" ==> fail
1094  *	host = "H1", user="U1" ==> fail
1095  *
1096  * Return 1 in case of a match, 0 otherwise.
1097  */
1098 int
1099 verifyIndexMatch(__nis_table_mapping_t *x, db_query *q,
1100 		__nis_rule_value_t *rv, char *name, char *val) {
1101 	int	i, j, k, match = 1;
1102 	char	*myself = "verifyIndexMatch";
1103 
1104 	/*
1105 	 * The pass and fail arrays are used by N2L to keep track of
1106 	 * index matches. This saves us from having matches in a
1107 	 * nested loop to decide OR or AND.
1108 	 */
1109 	int	ppos, fpos;
1110 	char	**pass, **fail;
1111 
1112 	if (x == 0)
1113 		return (0);
1114 
1115 	/* Trivial match */
1116 	if (x->index.numIndexes <= 0 || (!yp2ldap && q == 0))
1117 		return (1);
1118 
1119 	if (yp2ldap) {
1120 		if (!(pass = am(myself, x->index.numIndexes * sizeof (char *))))
1121 			return (0);
1122 		if (!(fail = am(myself,
1123 				x->index.numIndexes * sizeof (char *)))) {
1124 			sfree(pass);
1125 			return (0);
1126 		}
1127 		ppos = fpos = 0;
1128 	}
1129 
1130 	/* Check each index */
1131 	for (i = 0; i < x->index.numIndexes; i++) {
1132 		int	len = 0;
1133 		char	*value = 0;
1134 
1135 		/* Skip NULL index names */
1136 		if (x->index.name[i] == 0)
1137 			continue;
1138 
1139 		/* Check N2L values */
1140 		if (yp2ldap) {
1141 			if (name) {
1142 				if (strcasecmp(x->index.name[i], name) == 0)
1143 					value = val;
1144 				else
1145 					continue;
1146 			} else if (rv) {
1147 				if (strcasecmp(x->index.name[i], N2LKEY) == 0 ||
1148 					strcasecmp(x->index.name[i], N2LIPKEY)
1149 							== 0)
1150 					continue;
1151 				value = findVal(x->index.name[i], rv,
1152 							mit_nisplus);
1153 			}
1154 
1155 			if (value && verifyMappingMatch(x->index.value[i],
1156 									value))
1157 				pass[ppos++] = x->index.name[i];
1158 			else
1159 				fail[fpos++] = x->index.name[i];
1160 			continue;
1161 		}
1162 
1163 		/* If here, means nis+2ldap */
1164 
1165 		/* Is the index name a known column ? */
1166 		for (j = 0; j < x->numColumns; j++) {
1167 			if (strcmp(x->index.name[i], x->column[j]) == 0) {
1168 				/*
1169 				 * Do we have a value for the column ?
1170 				 */
1171 				for (k = 0; k < q->components.components_len;
1172 						k++) {
1173 					if (q->components.components_val[k].
1174 							which_index == j) {
1175 						value = q->components.
1176 							components_val[k].
1177 							index_value->
1178 							itemvalue.
1179 							itemvalue_val;
1180 						len = q->components.
1181 							components_val[k].
1182 							index_value->
1183 							itemvalue.
1184 							itemvalue_len;
1185 						break;
1186 					}
1187 				}
1188 				if (value != 0)
1189 					break;
1190 			}
1191 		}
1192 
1193 		/*
1194 		 * If we found a value, check if it matches the
1195 		 * format. If no value found or no match, this
1196 		 * mapping is _not_ an alternative. Otherwise,
1197 		 * we continue checking any other indexes.
1198 		 */
1199 		if (value == 0 ||
1200 			!verifyMappingMatch(x->index.value[i],
1201 				value)) {
1202 			match = 0;
1203 			break;
1204 		}
1205 	}
1206 
1207 	if (yp2ldap) {
1208 		for (--fpos; fpos >= 0; fpos--) {
1209 			for (i = 0; i < ppos; i++) {
1210 				if (strcmp(pass[i], fail[fpos]) == 0)
1211 					break;
1212 			}
1213 			if (i == ppos) {
1214 				match = 0;
1215 				break;
1216 			}
1217 		}
1218 		sfree(pass);
1219 		sfree(fail);
1220 	}
1221 
1222 	return (match);
1223 }
1224 
1225 /*
1226  * Return all table mappings that match the column values in 'q'.
1227  * If there's no match, return those alternative mappings that don't
1228  * have an index; if no such mapping exists, return NULL.
1229  *
1230  * If 'wantWrite' is set, we want mappings for writing (i.e., data
1231  * to LDAP); otherwise, we want mappings for reading.
1232  *
1233  * If 'wantObj' is set, we want object mappings only (i.e., _not_
1234  * those used to map entries in tables).
1235  *
1236  * If 'dbId' is non-NULL, we select mappings with a matching dbId field.
1237  */
1238 __nis_table_mapping_t **
1239 selectTableMapping(__nis_table_mapping_t *t, db_query *q,
1240 			int wantWrite, int wantObj, char *dbId,
1241 			int *numMatches) {
1242 	__nis_table_mapping_t	*r, *x, **tp;
1243 	int			i, j, k, nm, numap;
1244 	char			*myself = "selectTableMapping";
1245 
1246 	if (numMatches == 0)
1247 		numMatches = &nm;
1248 
1249 	/*
1250 	 * Count the number of possible mappings, so that we can
1251 	 * allocate the 'tp' array up front.
1252 	 */
1253 	for (numap = 0, x = t; x != 0; numap++, x = x->next);
1254 
1255 	if (numap == 0) {
1256 		*numMatches = 0;
1257 		return (0);
1258 	}
1259 
1260 	tp = am(myself, numap * sizeof (tp[0]));
1261 	if (tp == 0) {
1262 		*numMatches = -1;
1263 		return (0);
1264 	}
1265 
1266 	/*
1267 	 * Special cases:
1268 	 *
1269 	 *	q == 0 trivially matches any 't' of the correct object type
1270 	 *
1271 	 *	wantObj != 0 means we ignore 'q'
1272 	 */
1273 	if (q == 0 || wantObj) {
1274 		for (i = 0, x = t, nm = 0; i < numap; i++, x = x->next) {
1275 			if (x->objectDN == 0)
1276 				continue;
1277 			if (wantWrite) {
1278 				if (x->objectDN->write.scope ==
1279 						LDAP_SCOPE_UNKNOWN)
1280 					continue;
1281 			} else {
1282 				if (x->objectDN->read.scope ==
1283 						LDAP_SCOPE_UNKNOWN)
1284 					continue;
1285 			}
1286 			if (wantObj) {
1287 				if (x->numColumns > 0)
1288 					continue;
1289 			} else {
1290 				if (x->numColumns <= 0)
1291 					continue;
1292 			}
1293 			if (dbId != 0 && x->dbId != 0 &&
1294 					strcmp(dbId, x->dbId) != 0)
1295 				continue;
1296 			tp[nm] = x;
1297 			nm++;
1298 		}
1299 		*numMatches = nm;
1300 		if (nm == 0) {
1301 			sfree(tp);
1302 			tp = 0;
1303 		}
1304 		return (tp);
1305 	}
1306 
1307 	/* Scan all mappings, and collect candidates */
1308 	for (nm = 0, r = 0, x = t; x != 0; x = x->next) {
1309 		if (x->objectDN == 0)
1310 			continue;
1311 		if (wantWrite) {
1312 			if (x->objectDN->write.scope == LDAP_SCOPE_UNKNOWN)
1313 				continue;
1314 		} else {
1315 			if (x->objectDN->read.scope == LDAP_SCOPE_UNKNOWN)
1316 				continue;
1317 		}
1318 		/* Only want table/entry mappings */
1319 		if (x->numColumns <= 0)
1320 			continue;
1321 		if (dbId != 0 && x->dbId != 0 &&
1322 				strcmp(dbId, x->dbId) != 0)
1323 			continue;
1324 		/*
1325 		 * It's a match if: there are no indexes, or we actually
1326 		 * match the query with the indexes.
1327 		 */
1328 		if (x->index.numIndexes <= 0 ||
1329 					verifyIndexMatch(x, q, 0, 0, 0)) {
1330 			tp[nm] = x;
1331 			nm++;
1332 		}
1333 	}
1334 
1335 	if (nm == 0) {
1336 		free(tp);
1337 		tp = 0;
1338 	}
1339 
1340 	*numMatches = nm;
1341 
1342 	return (tp);
1343 }
1344 
1345 /*
1346  * Return 1 if there's an indexed mapping, 0 otherwise.
1347  */
1348 int
1349 haveIndexedMapping(__nis_table_mapping_t *t) {
1350 	__nis_table_mapping_t	*x;
1351 
1352 	for (x = t; x != 0; x = x->next) {
1353 		if (x->index.numIndexes > 0)
1354 			return (1);
1355 	}
1356 
1357 	return (0);
1358 }
1359 
1360 /*
1361  * Given an input string 'attrs' of the form "attr1=val1,attr2=val2,...",
1362  * or a filter, return the value associated with the attribute 'attrName'.
1363  * If no instance of 'attrName' is found, return 'default'. In all cases,
1364  * the return value is a copy, and must be freed by the caller.
1365  *
1366  * Of course, return NULL in case of failure.
1367  */
1368 static char *
1369 attrVal(char *msg, char *attrName, char *def, char *attrs) {
1370 	char	*val, *filter, **fc = 0;
1371 	int	i, nfc;
1372 	char	*myself = "attrVal";
1373 
1374 	if (attrName == 0 || attrs == 0)
1375 		return (0);
1376 
1377 	if (msg == 0)
1378 		msg = myself;
1379 
1380 	val = def;
1381 
1382 	filter = makeFilter(attrs);
1383 	if (filter != 0 && (fc = makeFilterComp(filter, &nfc)) != 0 &&
1384 			nfc > 0) {
1385 		for (i = 0; i < nfc; i++) {
1386 			char	*name, *value;
1387 
1388 			name = fc[i];
1389 			/* Skip if not of attr=value form */
1390 			if ((value = strchr(name, '=')) == 0)
1391 				continue;
1392 
1393 			*value = '\0';
1394 			value++;
1395 
1396 			if (strcasecmp(attrName, name) == 0) {
1397 				val = value;
1398 				break;
1399 			}
1400 		}
1401 	}
1402 
1403 	if (val != 0)
1404 		val = sdup(msg, T, val);
1405 
1406 	sfree(filter);
1407 	freeFilterComp(fc, nfc);
1408 
1409 	return (val);
1410 }
1411 
1412 extern bool_t	xdr_nis_object(register XDR *xdrs, nis_object *objp);
1413 
1414 /*
1415  * Copy an XDR:ed version of the NIS+ object 'o' (or the one indicated
1416  * by 't->objName' if 'o' is NULL) to the place indicated by
1417  * 't->objectDN->write'. Return an appropriate LDAP status code.
1418  */
1419 int
1420 objToLDAP(__nis_table_mapping_t *t, nis_object *o, entry_obj **ea, int numEa) {
1421 	__nis_table_mapping_t	**tp;
1422 	XDR			xdr;
1423 	char			*objName;
1424 	int			stat, osize, n, numMatches = 0;
1425 	void			*buf;
1426 	__nis_rule_value_t	*rv;
1427 	__nis_value_t		*val;
1428 	__nis_single_value_t	*sv;
1429 	char			**attrName, *dn;
1430 	char			*myself = "objToLDAP";
1431 
1432 	if (t == 0)
1433 		return (LDAP_PARAM_ERROR);
1434 
1435 	logmsg(MSG_NOTIMECHECK,
1436 #ifdef	NISDB_LDAP_DEBUG
1437 		LOG_WARNING,
1438 #else
1439 		LOG_INFO,
1440 #endif	/* NISDB_LDAP_DEBUG */
1441 		"%s: %s", myself, NIL(t->objName));
1442 
1443 	tp = selectTableMapping(t, 0, 1, 1, 0, &numMatches);
1444 	if (tp == 0 || numMatches <= 0) {
1445 		sfree(tp);
1446 		logmsg(MSG_NOTIMECHECK,
1447 #ifdef	NISDB_LDAP_DEBUG
1448 			LOG_WARNING,
1449 #else
1450 			LOG_INFO,
1451 #endif	/* NISDB_LDAP_DEBUG */
1452 			"%s: %s (no mapping)", myself, NIL(t->objName));
1453 		return (LDAP_SUCCESS);
1454 	}
1455 
1456 	for (n = 0; n < numMatches; n++) {
1457 
1458 		t = tp[n];
1459 
1460 		if (o == 0) {
1461 			sfree(tp);
1462 			return (LDAP_OPERATIONS_ERROR);
1463 		}
1464 
1465 		buf = (char *)xdrNisObject(o, ea, numEa, &osize);
1466 		if (buf == 0) {
1467 			sfree(tp);
1468 			return (LDAP_OPERATIONS_ERROR);
1469 		}
1470 
1471 		/*
1472 		 * Prepare to build a rule-value containing the XDR:ed
1473 		 * object
1474 		 */
1475 		rv = am(myself, sizeof (*rv));
1476 		sv = am(myself, sizeof (*sv));
1477 		val = am(myself, sizeof (*val));
1478 		attrName = am(myself, sizeof (attrName[0]));
1479 		if (attrName != 0)
1480 			attrName[0] = attrVal(myself, "nisplusObject",
1481 						"nisplusObject",
1482 						t->objectDN->write.attrs);
1483 		if (rv == 0 || sv == 0 || val == 0 || attrName == 0 ||
1484 				attrName[0] == 0) {
1485 			sfree(tp);
1486 			sfree(buf);
1487 			sfree(rv);
1488 			sfree(sv);
1489 			sfree(val);
1490 			sfree(attrName);
1491 			return (LDAP_NO_MEMORY);
1492 		}
1493 
1494 		sv->length = osize;
1495 		sv->value = buf;
1496 
1497 		/* 'vt_ber' just means "not a NUL-terminated string" */
1498 		val->type = vt_ber;
1499 		val->repeat = 0;
1500 		val->numVals = 1;
1501 		val->val = sv;
1502 
1503 		rv->numAttrs = 1;
1504 		rv->attrName = attrName;
1505 		rv->attrVal = val;
1506 
1507 		/*
1508 		 * The 'write.base' is the actual DN of the entry (and the
1509 		 * scope had better be 'base', but we don't check that).
1510 		 */
1511 		dn = t->objectDN->write.base;
1512 
1513 		stat = ldapModify(dn, rv, t->objectDN->write.attrs, 1);
1514 
1515 		freeRuleValue(rv, 1);
1516 
1517 	logmsg(MSG_NOTIMECHECK,
1518 #ifdef	NISDB_LDAP_DEBUG
1519 		LOG_WARNING,
1520 #else
1521 		LOG_INFO,
1522 #endif	/* NISDB_LDAP_DEBUG */
1523 		"%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat));
1524 
1525 		if (stat != LDAP_SUCCESS)
1526 			break;
1527 
1528 	}
1529 
1530 	sfree(tp);
1531 
1532 	return (stat);
1533 }
1534 
1535 /*
1536  * Retrieve a copy of the 't->objName' object from LDAP, where it's
1537  * stored in XDR:ed form in the place indicated by 't->objectDN->read'.
1538  * Un-XDR the object, and return a pointer to it in '*obj'; it's the
1539  * responsibility of the caller to free the object when it's no
1540  * longer needed.
1541  *
1542  * Returns an appropriate LDAP status.
1543  */
1544 int
1545 objFromLDAP(__nis_table_mapping_t *t, nis_object **obj,
1546 		entry_obj ***eaP, int *numEaP) {
1547 	__nis_table_mapping_t	**tp;
1548 	XDR			xdr;
1549 	nis_object		*o;
1550 	__nis_rule_value_t	*rv;
1551 	__nis_ldap_search_t	*ls;
1552 	char			*attrs[2], *filter, **fc = 0;
1553 	void			*buf;
1554 	int			i, j, nfc, nrv, blen, stat = LDAP_SUCCESS;
1555 	int			n, numMatches;
1556 	char			*myself = "objFromLDAP";
1557 
1558 	if (t == 0)
1559 		return (LDAP_PARAM_ERROR);
1560 
1561 	/*
1562 	 * If there's nowhere to store the result, we might as
1563 	 * well pretend all went well, and return right away.
1564 	 */
1565 	if (obj == 0)
1566 		return (LDAP_SUCCESS);
1567 
1568 	/* Prepare for the worst */
1569 	*obj = 0;
1570 
1571 	logmsg(MSG_NOTIMECHECK,
1572 #ifdef	NISDB_LDAP_DEBUG
1573 		LOG_WARNING,
1574 #else
1575 		LOG_INFO,
1576 #endif	/* NISDB_LDAP_DEBUG */
1577 		"%s: %s", myself, NIL(t->objName));
1578 
1579 	tp = selectTableMapping(t, 0, 0, 1, 0, &numMatches);
1580 	if (tp == 0 || numMatches <= 0) {
1581 		sfree(tp);
1582 		logmsg(MSG_NOTIMECHECK,
1583 #ifdef	NISDB_LDAP_DEBUG
1584 			LOG_WARNING,
1585 #else
1586 			LOG_INFO,
1587 #endif	/* NISDB_LDAP_DEBUG */
1588 			"%s: %s (no mapping)", myself, NIL(t->objName));
1589 		return (LDAP_SUCCESS);
1590 	}
1591 
1592 	for (n = 0; n < numMatches; n++) {
1593 
1594 		t = tp[n];
1595 
1596 		filter = makeFilter(t->objectDN->read.attrs);
1597 		if (filter == 0 || (fc = makeFilterComp(filter, &nfc)) == 0 ||
1598 				nfc <= 0) {
1599 			sfree(tp);
1600 			sfree(filter);
1601 			freeFilterComp(fc, nfc);
1602 			return ((t->objectDN->read.attrs != 0) ?
1603 				LDAP_NO_MEMORY : LDAP_PARAM_ERROR);
1604 		}
1605 		/* Don't need the filter, just the components */
1606 		sfree(filter);
1607 
1608 		/*
1609 		 * Look for a "nisplusObject" attribute, and (if found) copy
1610 		 * the value to attrs[0]. Also remove the "nisplusObject"
1611 		 * attribute and value from the filter components.
1612 		 */
1613 		attrs[0] = sdup(myself, T, "nisplusObject");
1614 		if (attrs[0] == 0) {
1615 			sfree(tp);
1616 			freeFilterComp(fc, nfc);
1617 			return (LDAP_NO_MEMORY);
1618 		}
1619 		attrs[1] = 0;
1620 		for (i = 0; i < nfc; i++) {
1621 			char	*name, *value;
1622 			int	compare;
1623 
1624 			name = fc[i];
1625 			/* Skip if not of attr=value form */
1626 			if ((value = strchr(name, '=')) == 0)
1627 				continue;
1628 
1629 			/* Temporarily overWrite the '=' with a '\0' */
1630 			*value = '\0';
1631 
1632 			/* Compare with our target attribute name */
1633 			compare = strcasecmp("nisplusObject", name);
1634 
1635 			/* Put back the '=' */
1636 			*value = '=';
1637 
1638 			/* Is it the name we're looking for ? */
1639 			if (compare == 0) {
1640 				sfree(attrs[0]);
1641 				attrs[0] = sdup(myself, T, value+1);
1642 				if (attrs[0] == 0) {
1643 					sfree(tp);
1644 					freeFilterComp(fc, nfc);
1645 					return (LDAP_NO_MEMORY);
1646 				}
1647 				sfree(fc[i]);
1648 				if (i < nfc-1)
1649 					(void) memmove(&fc[i], &fc[i+1],
1650 						(nfc-1-i) * sizeof (fc[i]));
1651 				nfc--;
1652 				break;
1653 			}
1654 		}
1655 
1656 		ls = buildLdapSearch(t->objectDN->read.base,
1657 					t->objectDN->read.scope,
1658 					nfc, fc, 0, attrs, 0, 1);
1659 		sfree(attrs[0]);
1660 		freeFilterComp(fc, nfc);
1661 		if (ls == 0) {
1662 			sfree(tp);
1663 			return (LDAP_OPERATIONS_ERROR);
1664 		}
1665 
1666 		nrv = 0;
1667 		rv = ldapSearch(ls, &nrv, 0, &stat);
1668 		if (rv == 0) {
1669 			sfree(tp);
1670 			freeLdapSearch(ls);
1671 			return (stat);
1672 		}
1673 
1674 		for (i = 0, buf = 0; i < nrv && buf == 0; i++) {
1675 			for (j = 0; j < rv[i].numAttrs; j++) {
1676 				if (strcasecmp(ls->attrs[0],
1677 					rv[i].attrName[j]) == 0) {
1678 					if (rv[i].attrVal[j].numVals <= 0)
1679 						continue;
1680 					buf = rv[i].attrVal[j].val[0].value;
1681 					blen = rv[i].attrVal[j].val[0].length;
1682 					break;
1683 				}
1684 			}
1685 		}
1686 
1687 		if (buf != 0) {
1688 			o = unXdrNisObject(buf, blen, eaP, numEaP);
1689 			if (o == 0) {
1690 				sfree(tp);
1691 				freeLdapSearch(ls);
1692 				freeRuleValue(rv, nrv);
1693 				return (LDAP_OPERATIONS_ERROR);
1694 			}
1695 			stat = LDAP_SUCCESS;
1696 			*obj = o;
1697 		} else {
1698 			stat = LDAP_NO_SUCH_OBJECT;
1699 		}
1700 
1701 		freeLdapSearch(ls);
1702 		freeRuleValue(rv, nrv);
1703 
1704 	logmsg(MSG_NOTIMECHECK,
1705 #ifdef	NISDB_LDAP_DEBUG
1706 		LOG_WARNING,
1707 #else
1708 		LOG_INFO,
1709 #endif	/* NISDB_LDAP_DEBUG */
1710 		"%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat));
1711 
1712 		if (stat != LDAP_SUCCESS)
1713 			break;
1714 
1715 	}
1716 
1717 	sfree(tp);
1718 
1719 	return (stat);
1720 }
1721 
1722 int
1723 deleteLDAPobj(__nis_table_mapping_t *t) {
1724 	__nis_table_mapping_t	**tp;
1725 	int		n, stat, numMatches = 0;
1726 	char		*myself = "deleteLDAPobj";
1727 
1728 	if (t == 0)
1729 		return (LDAP_PARAM_ERROR);
1730 
1731 	logmsg(MSG_NOTIMECHECK,
1732 #ifdef	NISDB_LDAP_DEBUG
1733 		LOG_WARNING,
1734 #else
1735 		LOG_INFO,
1736 #endif	/* NISDB_LDAP_DEBUG */
1737 		"%s: %s", myself, NIL(t->objName));
1738 
1739 	tp = selectTableMapping(t, 0, 1, 1, 0, &numMatches);
1740 	if (tp == 0 || numMatches <= 0) {
1741 		sfree(tp);
1742 		logmsg(MSG_NOTIMECHECK,
1743 #ifdef	NISDB_LDAP_DEBUG
1744 			LOG_WARNING,
1745 #else
1746 			LOG_INFO,
1747 #endif	/* NISDB_LDAP_DEBUG */
1748 			"%s: %s (no mapping)", myself, NIL(t->objName));
1749 		return (LDAP_SUCCESS);
1750 	}
1751 
1752 	for (n = 0; n < numMatches; n++) {
1753 
1754 		t = tp[n];
1755 
1756 		if (t->objectDN->delDisp == dd_always) {
1757 			/* Delete entire entry */
1758 			stat = ldapModify(t->objectDN->write.base, 0,
1759 					t->objectDN->write.attrs, 1);
1760 		} else if (t->objectDN->delDisp == dd_perDbId) {
1761 			/*
1762 			 * Delete the attribute holding the object.
1763 			 * First, determine what that attribute is called.
1764 			 */
1765 			char			*attrName =
1766 						attrVal(myself,
1767 							"nisplusObject",
1768 							"nisplusObject",
1769 						t->objectDN->write.attrs);
1770 			__nis_rule_value_t	rv;
1771 			__nis_value_t		val;
1772 
1773 			if (attrName == 0) {
1774 				sfree(tp);
1775 				return (LDAP_NO_MEMORY);
1776 			}
1777 
1778 			/*
1779 			 * Build a __nis_value_t with 'numVals' < 0 to
1780 			 * indicate deletion.
1781 			 */
1782 			val.type = vt_ber;
1783 			val.numVals = -1;
1784 			val.val = 0;
1785 
1786 			/*
1787 			 * Build a rule-value with the name we determined
1788 			 * above, and the deletion value.
1789 			 */
1790 			(void) memset(&rv, 0, sizeof (rv));
1791 			rv.numAttrs = 1;
1792 			rv.attrName = &attrName;
1793 			rv.attrVal = &val;
1794 
1795 			stat = ldapModify(t->objectDN->write.base, &rv,
1796 						t->objectDN->write.attrs, 0);
1797 
1798 			sfree(attrName);
1799 		} else if (t->objectDN->delDisp == dd_never) {
1800 			/* Nothing to do, so we're trivially successful */
1801 			stat = LDAP_SUCCESS;
1802 		} else {
1803 			stat = LDAP_PARAM_ERROR;
1804 		}
1805 
1806 	logmsg(MSG_NOTIMECHECK,
1807 #ifdef	NISDB_LDAP_DEBUG
1808 		LOG_WARNING,
1809 #else
1810 		LOG_INFO,
1811 #endif	/* NISDB_LDAP_DEBUG */
1812 		"%s: %s (%s)", myself, NIL(t->objName), ldap_err2string(stat));
1813 
1814 		/* If there were no such object, we've trivially succeeded */
1815 		if (stat == LDAP_NO_SUCH_OBJECT)
1816 			stat = LDAP_SUCCESS;
1817 
1818 		if (stat != LDAP_SUCCESS)
1819 			break;
1820 
1821 	}
1822 
1823 	sfree(tp);
1824 
1825 	return (stat);
1826 }
1827