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