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.
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.
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]
22 * Copyright 2015 Gary Mills
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 #include <sys/types.h>
32 #include <rpcsvc/nis.h>
35 #include "ldap_util.h"
36 #include "ldap_attr.h"
37 #include "ldap_ruleval.h"
40 #include "ldap_glob.h"
44 /* From yptol/dit_access_utils.h */
45 #define N2LKEY "rf_key"
46 #define N2LIPKEY "rf_ipkey"
48 __nis_hash_table_mt ldapMappingList
= NIS_HASH_TABLE_MT_INIT
;
53 setColumnNames(__nis_table_mapping_t
*t
) {
56 char *myself
= "setColumnNames";
62 nic
= (col
!= 0) ? t
->numColumns
: -1;
64 t
->objType
= NIS_BOGUS_OBJ
;
68 * If it's a table object, but there are no translation rules,
69 * this mapping is for the table object itself. In that case,
70 * we throw away the column names (if any).
72 if (t
->objType
== NIS_TABLE_OBJ
&& t
->numRulesFromLDAP
== 0 &&
73 t
->numRulesToLDAP
== 0) {
74 for (i
= 0; i
< t
->numColumns
; i
++)
83 * Verify that all column names found by the parser
84 * are present in the actual column list.
87 for (i
= 0, noc
= 0; i
< nic
; i
++) {
92 /* Skip the 'zo_*' special column names */
93 if (isObjAttrString(col
[i
]))
95 for (j
= 0; j
< t
->numColumns
; j
++) {
96 if (strcmp(col
[i
], t
->column
[j
]) == 0) {
103 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
104 "%s: No column \"%s\" in \"%s\"",
105 myself
, NIL(col
[i
]), NIL(t
->objName
));
110 /* Remove any setup by the parser */
111 for (i
= 0; i
< nic
; i
++) {
120 freeSingleObjAttr(__nis_obj_attr_t
*attr
) {
124 sfree(attr
->zo_owner
);
125 sfree(attr
->zo_group
);
126 sfree(attr
->zo_domain
);
131 freeObjAttr(__nis_obj_attr_t
**attr
, int numAttr
) {
137 for (i
= 0; i
< numAttr
; i
++) {
138 freeSingleObjAttr(attr
[i
]);
145 cloneObjAttr(__nis_obj_attr_t
*old
) {
146 __nis_obj_attr_t
*new;
147 char *myself
= "cloneObjAttr";
152 new = am(myself
, sizeof (*new));
156 new->zo_owner
= sdup(myself
, T
, old
->zo_owner
);
157 if (new->zo_owner
== 0 && old
->zo_owner
!= 0)
160 new->zo_group
= sdup(myself
, T
, old
->zo_group
);
161 if (new->zo_group
== 0 && old
->zo_group
!= 0)
164 new->zo_domain
= sdup(myself
, T
, old
->zo_domain
);
165 if (new->zo_domain
== 0 && old
->zo_domain
!= 0)
168 new->zo_access
= old
->zo_access
;
169 new->zo_ttl
= old
->zo_ttl
;
174 freeSingleObjAttr(new);
181 * Obtain NIS+ entries (in the form of db_query's) from the supplied table
182 * mapping and db_query.
184 * If 'qin' is NULL, enumeration is desired.
186 * On exit, '*numQueries' contains the number of (db_query *)'s in the
187 * return array, '*ldapStat' the LDAP operation status, and '*objAttr'
188 * a pointer to an array (of '*numQueries elements) of object attributes
189 * (zo_owner, etc.). If no object attributes were retrieved, '*objAttr'
190 * is NULL; any and all of the (*objAttr)[i]'s may be NULL.
193 mapFromLDAP(__nis_table_mapping_t
*t
, db_query
*qin
, int *numQueries
,
194 char *dbId
, int *ldapStat
, __nis_obj_attr_t
***objAttr
) {
195 __nis_table_mapping_t
**tp
;
197 __nis_rule_value_t
*rv
;
198 __nis_ldap_search_t
*ls
;
199 int n
, numVals
, numMatches
= 0;
201 __nis_obj_attr_t
**attr
;
202 char *myself
= "mapFromLDAP";
207 if (t
== 0 || numQueries
== 0) {
208 *ldapStat
= LDAP_PARAM_ERROR
;
212 /* Select the correct table mapping(s) */
213 tp
= selectTableMapping(t
, qin
, 0, 0, dbId
, &numMatches
);
214 if (tp
== 0 || numMatches
<= 0) {
216 * Not really an error; just no matching mapping
219 *ldapStat
= LDAP_SUCCESS
;
226 /* For each mapping */
227 for (numVals
= 0, n
= 0; n
< numMatches
; n
++) {
229 int i
, nqt
= 0, filterOnQin
, res
= 0;
234 rv
= buildNisPlusRuleValue(t
, qin
, 0);
237 * Depending on the value of res, we shall
238 * proceed to next table mapping.
240 ls
= createLdapRequest(t
, rv
, 0, 1, &res
, NULL
);
245 /* Build enumeration request */
247 ls
= createLdapRequest(t
, 0, 0, 1, NULL
, NULL
);
250 freeRuleValue(rv
, 1);
254 * if the res is NP_LDAP_RULES_NO_VALUE, that means we
255 * have enough NIS+ columns for the rules to produce
256 * values, but none of them did, so continue to the
257 * next table mapping. Otherwise do cleanup and return
260 if (res
== NP_LDAP_RULES_NO_VALUE
)
262 for (i
= 0; i
< numVals
; i
++)
266 *ldapStat
= LDAP_OPERATIONS_ERROR
;
271 nqt
= (ls
->isDN
|| qin
!= 0) ? 0 : -1;
272 rv
= ldapSearch(ls
, &nqt
, 0, ldapStat
);
275 * If qin != 0, then we need to make sure that the
276 * LDAP search is filtered so that only entries that
277 * are compatible with 'qin' are retained. This will
278 * happen automatically if we do a DN search (in which
279 * case, no need to filter on 'qin').
281 if (ls
->isDN
|| qin
== 0)
288 /* Convert rule-values to db_query's */
289 if (rv
!= 0 && nqt
> 0) {
291 __nis_obj_attr_t
**at
= 0;
293 qt
= ruleValue2Query(t
, rv
,
294 (filterOnQin
) ? qin
: 0, &at
, &nqt
);
295 freeRuleValue(rv
, nrv
);
297 if (qt
!= 0 && q
== 0) {
301 } else if (qt
!= 0) {
303 __nis_obj_attr_t
**atmp
;
305 /* Extend the 'q' array */
306 tmp
= reallocarray(q
, numVals
+ nqt
,
308 /* ... and the 'attr' array */
309 atmp
= reallocarray(attr
, numVals
+ nqt
,
311 if (tmp
== 0 || atmp
== 0) {
312 logmsg(MSG_NOMEM
, LOG_ERR
,
313 "%s: realloc(%d) => NULL",
315 (numVals
+nqt
) * sizeof (q
[0]));
316 for (i
= 0; i
< numVals
; i
++)
318 for (i
= 0; i
< nqt
; i
++)
325 freeObjAttr(at
, nqt
);
326 freeObjAttr(attr
, numVals
);
327 *ldapStat
= LDAP_NO_MEMORY
;
332 /* Add the results for this 't' */
333 (void) memcpy(&q
[numVals
], qt
,
334 nqt
* sizeof (qt
[0]));
335 (void) memcpy(&attr
[numVals
], at
,
336 nqt
* sizeof (at
[0]));
345 *numQueries
= numVals
;
349 freeObjAttr(attr
, numVals
);
356 * Add the object attributes (zo_owner, etc.) to the rule-value 'rv'.
357 * Returns a pointer to the (possibly newly allocated) rule-value,
358 * or NULL in case of failure. If not returning 'rvIn', the latter
359 * will have been freed.
362 addObjAttr2RuleValue(nis_object
*obj
, __nis_rule_value_t
*rvIn
) {
363 __nis_rule_value_t
*rv
;
364 char abuf
[2 * sizeof (obj
->zo_access
) + 1];
365 char tbuf
[2 * sizeof (obj
->zo_ttl
) + 1];
373 rv
= initRuleValue(1, 0);
378 if (obj
->zo_owner
!= 0) {
379 if (addSCol2RuleValue("zo_owner", obj
->zo_owner
, rv
) != 0) {
380 freeRuleValue(rv
, 1);
385 if (obj
->zo_group
!= 0) {
386 if (addSCol2RuleValue("zo_group", obj
->zo_group
, rv
) != 0) {
387 freeRuleValue(rv
, 1);
392 if (obj
->zo_domain
!= 0) {
393 if (addSCol2RuleValue("zo_domain", obj
->zo_domain
, rv
) != 0) {
394 freeRuleValue(rv
, 1);
399 (void) memset(abuf
, 0, sizeof (abuf
));
400 (void) memset(tbuf
, 0, sizeof (tbuf
));
402 sprintf(abuf
, "%x", obj
->zo_access
);
403 sprintf(tbuf
, "%x", obj
->zo_ttl
);
405 if (addSCol2RuleValue("zo_access", abuf
, rv
) != 0) {
406 freeRuleValue(rv
, 1);
409 if (addSCol2RuleValue("zo_ttl", tbuf
, rv
) != 0) {
410 freeRuleValue(rv
, 1);
418 * Returns a pointer to (NOT a copy of) the value for the specified
419 * column 'col' in the rule-value 'rv'.
422 findColValue(char *col
, __nis_rule_value_t
*rv
) {
425 if (col
== 0 || rv
== 0 || rv
->numColumns
<= 0)
428 for (i
= 0; i
< rv
->numColumns
; i
++) {
429 if (strcmp(col
, rv
->colName
[i
]) == 0)
430 return (&rv
->colVal
[i
]);
437 * Return the NIS+ object attributes (if any) in the rule-value 'rv'.
440 ruleValue2ObjAttr(__nis_rule_value_t
*rv
) {
441 __nis_obj_attr_t
*attr
;
443 char *myself
= "ruleValue2ObjAttr";
445 if (rv
== 0 || rv
->numColumns
<= 0)
448 attr
= am(myself
, sizeof (*attr
));
450 if ((val
= findColValue("zo_owner", rv
)) != 0 &&
451 val
->type
== vt_string
&& val
->numVals
== 1 &&
452 val
->val
[0].value
!= 0) {
453 attr
->zo_owner
= sdup(myself
, T
, val
->val
[0].value
);
454 if (attr
->zo_owner
== 0) {
455 freeSingleObjAttr(attr
);
460 if ((val
= findColValue("zo_group", rv
)) != 0 &&
461 val
->type
== vt_string
&& val
->numVals
== 1 &&
462 val
->val
[0].value
!= 0) {
463 attr
->zo_group
= sdup(myself
, T
, val
->val
[0].value
);
464 if (attr
->zo_group
== 0) {
465 freeSingleObjAttr(attr
);
470 if ((val
= findColValue("zo_domain", rv
)) != 0 &&
471 val
->type
== vt_string
&& val
->numVals
== 1 &&
472 val
->val
[0].value
!= 0) {
473 attr
->zo_domain
= sdup(myself
, T
, val
->val
[0].value
);
474 if (attr
->zo_domain
== 0) {
475 freeSingleObjAttr(attr
);
480 if ((val
= findColValue("zo_access", rv
)) != 0 &&
481 val
->type
== vt_string
&& val
->numVals
== 1 &&
482 val
->val
[0].value
!= 0) {
483 if (sscanf(val
->val
[0].value
, "%x", &attr
->zo_access
) != 1) {
484 freeSingleObjAttr(attr
);
489 if ((val
= findColValue("zo_ttl", rv
)) != 0 &&
490 val
->type
== vt_string
&& val
->numVals
== 1 &&
491 val
->val
[0].value
!= 0) {
492 if (sscanf(val
->val
[0].value
, "%x", &attr
->zo_ttl
) != 1) {
493 freeSingleObjAttr(attr
);
502 * If the supplied string is one of the object attributes, return one.
503 * Otherwise, return zero.
506 isObjAttrString(char *str
) {
510 if (strcmp("zo_owner", str
) == 0 ||
511 strcmp("zo_group", str
) == 0 ||
512 strcmp("zo_domain", str
) == 0 ||
513 strcmp("zo_access", str
) == 0 ||
514 strcmp("zo_ttl", str
) == 0)
522 * If the supplied value is one of the object attribute strings, return
523 * a pointer to the string. Otherwise, return NULL.
526 isObjAttr(__nis_single_value_t
*val
) {
527 if (val
== 0 || val
->length
<= 0 || val
->value
== 0)
530 if (isObjAttrString(val
->value
))
537 setObjAttrField(char *attrName
, __nis_single_value_t
*val
,
538 __nis_obj_attr_t
**objAttr
) {
539 __nis_obj_attr_t
*attr
;
540 char *myself
= "setObjAttrField";
542 if (attrName
== 0 || val
== 0 || objAttr
== 0 ||
543 val
->value
== 0 || val
->length
<= 0)
549 attr
= am(myself
, sizeof (*attr
));
555 if (strcmp("zo_owner", attrName
) == 0) {
556 if (attr
->zo_owner
== 0) {
557 attr
->zo_owner
= sdup(myself
, T
, val
->value
);
558 if (attr
->zo_owner
== 0)
561 } else if (strcmp("zo_group", attrName
) == 0) {
562 if (attr
->zo_group
== 0) {
563 attr
->zo_group
= sdup(myself
, T
, val
->value
);
564 if (attr
->zo_group
== 0)
567 } else if (strcmp("zo_domain", attrName
) == 0) {
568 if (attr
->zo_domain
== 0) {
569 attr
->zo_domain
= sdup(myself
, T
, val
->value
);
570 if (attr
->zo_domain
== 0)
573 } else if (strcmp("zo_access", attrName
) == 0) {
574 if (attr
->zo_access
== 0) {
575 if (sscanf(val
->value
, "%x", &attr
->zo_access
) != 1)
578 } else if (strcmp("zo_ttl", attrName
) == 0) {
579 if (attr
->zo_ttl
== 0) {
580 if (sscanf(val
->value
, "%x", &attr
->zo_ttl
) != 1)
589 * Return a DN and rule-value for the supplied mapping, db_query's, and
590 * input rule-value. This function only works on a single mapping. See
591 * mapToLDAP() below for a description of the action depending on the
592 * values of 'old' and 'new'.
594 * If both 'old' and 'new' are supplied, and the modify would result
595 * in a change to the DN, '*oldDN' will contain the old DN. Otherwise
596 * (and normally), '*oldDN' will be NULL.
599 map1qToLDAP(__nis_table_mapping_t
*t
, db_query
*old
, db_query
*new,
600 __nis_rule_value_t
*rvIn
, __nis_rule_value_t
**rvOutP
,
603 __nis_rule_value_t
*rv
, *rvt
;
604 __nis_ldap_search_t
*ls
;
605 char *dn
= 0, *oldDn
= 0;
606 __nis_table_mapping_t del
;
607 char *myself
= "map1qToLDAP";
609 if (t
== 0 || (old
== 0 && new == 0) || rvOutP
== 0)
613 * If entry should be deleted, we look at the delete
614 * policy in the table mapping. Should it specify a
615 * rule set, we use that rule set to build a rule-
616 * value, and the delete actually becomes a modify
619 if (old
!= 0 && new == 0) {
620 if (t
->objectDN
->delDisp
== dd_perDbId
) {
622 * The functions that build a rule-value from a
623 * rule set expect a __nis_table_mapping_t, but the
624 * rule set in the __nis_object_dn_t isn't of that
625 * form. So, build a pseudo-__nis_table_mapping_t that
626 * borrows heavily from 't'.
630 del
.numRulesToLDAP
= del
.objectDN
->numDbIds
;
631 del
.ruleToLDAP
= del
.objectDN
->dbId
;
634 * Do a modify with the pseudo-table
635 * mapping, and the 'old' db_query
636 * supplying input to the delete rule
641 } else if (t
->objectDN
->delDisp
== dd_always
) {
643 /* Nothing to do here; all handled below */
645 } else if (t
->objectDN
->delDisp
== dd_never
) {
651 logmsg(MSG_INVALIDDELDISP
, LOG_WARNING
,
652 "%s: Invalid delete disposition %d for \"%s\"",
653 myself
, t
->objectDN
->delDisp
,
660 /* Make a copy of the input rule-value */
662 rv
= initRuleValue(1, rvIn
);
669 /* First get a rule-value from the supplied NIS+ entry. */
671 rv
= buildNisPlusRuleValue(t
, ((old
!= 0) ? old
: new), rvt
);
672 freeRuleValue(rvt
, 1);
674 logmsg(MSG_NORULEVALUE
, LOG_WARNING
,
675 "%s: No in-query rule-value derived for \"%s\"",
676 myself
, NIL(t
->dbId
));
681 * Create a request (really only care about the DN) from the
682 * supplied NIS+ entry data.
684 ls
= createLdapRequest(t
, rv
, &dn
, 0, NULL
, NULL
);
685 if (ls
== 0 || dn
== 0) {
686 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
687 "%s: Unable to create LDAP request for %s: %s",
688 myself
, NIL(t
->dbId
),
689 (dn
!= 0) ? dn
: rvId(rv
, mit_nisplus
));
692 freeRuleValue(rv
, 1);
700 * Create a rule-value from the new NIS+ entry.
701 * Don't want to mix in the rule-value derived
702 * from 'old', so delete it. However, we still
703 * want the owner, group, etc., from 'rvIn'.
706 freeRuleValue(rv
, 1);
708 rv
= initRuleValue(1, rvIn
);
718 rv
= buildNisPlusRuleValue(t
, new, rvt
);
719 freeRuleValue(rvt
, 1);
721 logmsg(MSG_NORULEVALUE
, LOG_WARNING
,
722 "%s: No new rule-value derived for \"%s: %s\"",
723 myself
, NIL(t
->dbId
), dn
);
728 * Check if the proposed modification would result in a
729 * a change to the DN.
734 ls
= createLdapRequest(t
, rv
, &dn
, 0, NULL
, NULL
);
735 if (ls
== 0 || dn
== 0) {
736 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
737 "%s: Unable to create new DN for \"%s: %s\"",
738 myself
, NIL(t
->dbId
), oldDn
);
741 freeRuleValue(rv
, 1);
745 if (strcasecmp(oldDn
, dn
) == 0) {
761 * Since the DN hash list is an automatic variable, there's no need for
762 * locking, and we remove the locking overhead by using the libnsl
766 #undef NIS_HASH_TABLE
775 * Update LDAP per the supplied table mapping and db_query's.
777 * 'nq' is the number of elements in the 'old', 'new', and 'rvIn'
778 * arrays. mapToLDAP() generally performs one update for each
779 * element; however, if one or more of the individual queries
780 * produce the same DN, they're merged into a single update.
782 * There are four cases, depending on the values of 'old[iq]' and
785 * (1) old[iq] == 0 && new[iq] == 0
786 * No action; skip to next query
788 * (2) old[iq] == 0 && new[iq] != 0
789 * Attempt to use the 'new' db_query to get a DN, and try to create
790 * the corresponding LDAP entry.
792 * (3) old[iq] != 0 && new[iq] == 0
793 * Use the 'old' db_query to get a DN, and try to delete the LDAP
794 * entry per the table mapping.
796 * (4) old[iq] != 0 && new[iq] != 0
797 * Use the 'old' db_query to get a DN, and update (possibly create)
798 * the corresponding LDAP entry per the 'new' db_query.
800 * If 'rvIn' is non-NULL, it is expected to contain the object attributes
801 * (zo_owner, etc.) to be written to LDAP. 'rvIn' is an array with 'nq'
804 * If 'firstOnly' is set, only the first old[iq]/new[iq] pair is used
805 * to perform the actual update. Any additional queries specified will
806 * have their values folded in, but are not used to derive update targets.
807 * This mode is inteded to support the case where multiple NIS+ entries
808 * map to one and the same LDAP entry. Note that 'rvIn' must still be
809 * an array of 'nq' elements, though if 'firstOnly' is set, it should be
810 * OK to leave all but 'rvIn[0]' empty.
812 * 'dbId' is used to further narow down the selection of mapping candidates
813 * to those matching the 'dbId' value.
816 mapToLDAP(__nis_table_mapping_t
*tm
, int nq
, db_query
**old
, db_query
**new,
817 __nis_rule_value_t
*rvIn
, int firstOnly
, char *dbId
) {
818 __nis_table_mapping_t
**tp
, **tpa
;
819 int i
, n
, rnq
, iq
, r
, ret
= LDAP_SUCCESS
;
820 int maxMatches
, numMatches
= 0;
821 __nis_ldap_search_t
*ls
;
822 char **dn
= 0, **odn
= 0;
823 __nis_rule_value_t
**rv
;
825 char *myself
= "mapToLDAP";
828 if (tm
== 0 || (old
== 0 && new == 0) || nq
<= 0)
829 return (LDAP_PARAM_ERROR
);
831 /* Determine maximum number of table mapping matches */
833 tp
= selectTableMapping(tm
,
834 (old
!= 0 && old
[0] != 0) ? old
[0] : new[0], 1, 0,
836 numMatches
= maxMatches
;
838 tp
= selectTableMapping(tm
, 0, 1, 0, dbId
, &maxMatches
);
842 * If no matching mapping, we're not mapping to LDAP in this
845 if (tp
== 0 || maxMatches
== 0) {
847 return (LDAP_SUCCESS
);
851 * Allocate the 'rv', 'dn', and 'tpa' arrays. Worst case is that
852 * we need nq * maxMatches elements in each array. However, if
853 * 'firstOnly' is set, we only need one element per matching
856 dn
= am(myself
, (firstOnly
? 1 : nq
) * maxMatches
* sizeof (dn
[0]));
857 odn
= am(myself
, (firstOnly
? 1 : nq
) * maxMatches
* sizeof (odn
[0]));
858 rv
= am(myself
, (firstOnly
? 1 : nq
) * maxMatches
* sizeof (rv
[0]));
859 tpa
= am(myself
, (firstOnly
? 1 : nq
) * maxMatches
* sizeof (tpa
[0]));
860 if (dn
== 0 || odn
== 0 || rv
== 0 || tpa
== 0) {
866 return (LDAP_NO_MEMORY
);
869 /* Unless nq == 1, we don't need the 'tp' value */
873 logmsg(MSG_NOTIMECHECK
,
874 #ifdef NISDB_LDAP_DEBUG
878 #endif /* NISDB_LDAP_DEBUG */
879 "%s: %s: %d * %d potential updates",
880 myself
, NIL(tm
->objName
), nq
, maxMatches
);
883 * Create DNs, column and attribute values, and merge duplicate DNs.
885 for (iq
= 0, rnq
= 0; iq
< nq
; iq
++) {
888 if ((old
== 0 || old
[iq
] == 0) &&
889 (new == 0 || new[iq
] == 0))
893 * Select matching table mappings; if nq == 1, we've already
894 * got the 'tp' array from above. We expect this to be the
895 * most common case, so it's worth special treatment.
898 tp
= selectTableMapping(tm
,
899 (old
!= 0 && old
[iq
] != 0) ? old
[iq
] : new[iq
], 1, 0,
903 else if (numMatches
<= 0) {
908 idx
= iq
* maxMatches
;
910 if (idx
== 0 || !firstOnly
)
911 (void) memcpy(&tpa
[idx
], tp
,
912 numMatches
* sizeof (tpa
[idx
]));
914 for (n
= 0; n
< numMatches
; n
++) {
916 __nis_rule_value_t
*rvt
= 0;
921 dnt
= map1qToLDAP(tp
[n
],
922 (old
!= 0) ? old
[iq
] : 0,
923 (new != 0) ? new[iq
] : 0,
924 (rvIn
!= 0) ? &rvIn
[iq
] : 0,
930 #ifdef NISDB_LDAP_DEBUG
936 #endif /* NISDB_LDAP_DEBUG */
940 * Create a request to get a rule-value with
941 * NIS+ data translated to LDAP equivalents.
943 ls
= createLdapRequest(tp
[n
], rvt
, 0, 0, NULL
, NULL
);
945 if (ret
== LDAP_SUCCESS
)
946 ret
= LDAP_OPERATIONS_ERROR
;
947 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
948 "%s: Unable to map to LDAP attrs for %s:dn=%s",
949 myself
, NIL(tp
[n
]->dbId
), dnt
);
951 freeRuleValue(rvt
, 1);
957 * If the DN is the same as one we already know
958 * about, merge the rule-values.
961 if ((iq
== 0 || !firstOnly
) && dnt
!= 0) {
962 dni
= am(myself
, sizeof (*dni
));
964 dni
->item
.name
= dnt
;
965 dni
->index
= idx
+ n
;
968 logmsg(MSG_NOTIMECHECK
, LOG_WARNING
,
969 "%s: Skipping update for dn=\"%s\"",
980 freeRuleValue(rvt
, 1);
983 } else if (dnt
!= 0) {
986 freeRuleValue(rvt
, 1);
992 logmsg(MSG_NOTIMECHECK
,
993 #ifdef NISDB_LDAP_DEBUG
997 #endif /* NISDB_LDAP_DEBUG */
998 "%s: %s: %d update%s requested",
999 myself
, NIL(tm
->objName
), rnq
, rnq
!= 1 ? "s" : "");
1001 /* Perform the updates */
1002 for (i
= rnq
= 0; i
< (firstOnly
? maxMatches
: nq
*maxMatches
); i
++) {
1008 #ifdef NISDB_LDAP_DEBUG
1009 logmsg(MSG_NOTIMECHECK
, LOG_INFO
,
1012 (new != 0 && new[i
/maxMatches
] != 0) ?
1013 "modify" : "delete",
1014 NIL(tpa
[i
]->dbId
), dn
[i
]);
1015 #endif /* NISDB_LDAP_DEBUG */
1017 delPerDbId
= (tpa
[i
]->objectDN
->delDisp
== dd_perDbId
);
1018 if ((new != 0 && new[i
/maxMatches
] != 0) || delPerDbId
) {
1020 * Try to modify/create the specified DN. First,
1021 * however, if the update changes the DN, make
1024 if (odn
[i
] == 0 || (r
= ldapChangeDN(odn
[i
], dn
[i
])) ==
1028 addFirst
= (new != 0 &&
1029 new[i
/maxMatches
] != 0 &&
1031 r
= ldapModify(dn
[i
], rv
[i
],
1032 tpa
[i
]->objectDN
->write
.attrs
,
1036 /* Try to delete the specified DN */
1037 r
= ldapModify(dn
[i
], 0,
1038 tpa
[i
]->objectDN
->write
.attrs
, 0);
1041 if (r
== LDAP_SUCCESS
) {
1044 if (ret
== LDAP_SUCCESS
)
1046 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
1047 "%s: LDAP %s request error %d for %s:dn=%s",
1049 (new != 0 && new[i
/maxMatches
] != 0) ?
1050 "modify" : "delete",
1051 r
, NIL(tpa
[i
]->dbId
), dn
[i
]);
1056 freeRuleValue(rv
[i
], 1);
1065 logmsg(MSG_NOTIMECHECK
,
1066 #ifdef NISDB_LDAP_DEBUG
1070 #endif /* NISDB_LDAP_DEBUG */
1071 "%s: %s: %d update%s performed",
1072 myself
, NIL(tm
->objName
), rnq
, rnq
!= 1 ? "s" : "");
1078 * In nis+2ldap, check if the query 'q' matches the selector index 'x->index'.
1080 * In nis2ldap, if 'name' is provided then check if its value in 'val'
1081 * matches the selector index. If 'name' is NULL, then check if rule-value 'rv'
1082 * matches the index.
1083 * To match the selector index, all fieldspecs in the indexlist should match
1084 * (AND). In nis2ldap, an exception is, if there are multiple fieldspecs with
1085 * the same fieldname then only one of them needs to match (OR).
1087 * Indexlist = [host="H*", host="I*", user="U*", domain="D*"]
1089 * host = "H1", user="U1", domain="D1" ==> pass
1090 * host = "I1", user="U1", domain="D1" ==> pass
1091 * host = "X1", user="U1", domain="D1" ==> fail
1092 * host = "H1", user="X1", domain="D1" ==> fail
1093 * host = "H1", user="U1" ==> fail
1095 * Return 1 in case of a match, 0 otherwise.
1098 verifyIndexMatch(__nis_table_mapping_t
*x
, db_query
*q
,
1099 __nis_rule_value_t
*rv
, char *name
, char *val
) {
1100 int i
, j
, k
, match
= 1;
1101 char *myself
= "verifyIndexMatch";
1104 * The pass and fail arrays are used by N2L to keep track of
1105 * index matches. This saves us from having matches in a
1106 * nested loop to decide OR or AND.
1109 char **pass
, **fail
;
1115 if (x
->index
.numIndexes
<= 0 || (!yp2ldap
&& q
== 0))
1119 if (!(pass
= am(myself
, x
->index
.numIndexes
* sizeof (char *))))
1121 if (!(fail
= am(myself
,
1122 x
->index
.numIndexes
* sizeof (char *)))) {
1129 /* Check each index */
1130 for (i
= 0; i
< x
->index
.numIndexes
; i
++) {
1133 /* Skip NULL index names */
1134 if (x
->index
.name
[i
] == 0)
1137 /* Check N2L values */
1140 if (strcasecmp(x
->index
.name
[i
], name
) == 0)
1145 if (strcasecmp(x
->index
.name
[i
], N2LKEY
) == 0 ||
1146 strcasecmp(x
->index
.name
[i
], N2LIPKEY
)
1149 value
= findVal(x
->index
.name
[i
], rv
,
1153 if (value
&& verifyMappingMatch(x
->index
.value
[i
],
1155 pass
[ppos
++] = x
->index
.name
[i
];
1157 fail
[fpos
++] = x
->index
.name
[i
];
1161 /* If here, means nis+2ldap */
1163 /* Is the index name a known column ? */
1164 for (j
= 0; j
< x
->numColumns
; j
++) {
1165 if (strcmp(x
->index
.name
[i
], x
->column
[j
]) == 0) {
1167 * Do we have a value for the column ?
1169 for (k
= 0; k
< q
->components
.components_len
;
1171 if (q
->components
.components_val
[k
].
1173 value
= q
->components
.
1187 * If we found a value, check if it matches the
1188 * format. If no value found or no match, this
1189 * mapping is _not_ an alternative. Otherwise,
1190 * we continue checking any other indexes.
1193 !verifyMappingMatch(x
->index
.value
[i
],
1201 for (--fpos
; fpos
>= 0; fpos
--) {
1202 for (i
= 0; i
< ppos
; i
++) {
1203 if (strcmp(pass
[i
], fail
[fpos
]) == 0)
1219 * Return all table mappings that match the column values in 'q'.
1220 * If there's no match, return those alternative mappings that don't
1221 * have an index; if no such mapping exists, return NULL.
1223 * If 'wantWrite' is set, we want mappings for writing (i.e., data
1224 * to LDAP); otherwise, we want mappings for reading.
1226 * If 'wantObj' is set, we want object mappings only (i.e., _not_
1227 * those used to map entries in tables).
1229 * If 'dbId' is non-NULL, we select mappings with a matching dbId field.
1231 __nis_table_mapping_t
**
1232 selectTableMapping(__nis_table_mapping_t
*t
, db_query
*q
,
1233 int wantWrite
, int wantObj
, char *dbId
,
1235 __nis_table_mapping_t
*x
, **tp
;
1237 char *myself
= "selectTableMapping";
1239 if (numMatches
== 0)
1243 * Count the number of possible mappings, so that we can
1244 * allocate the 'tp' array up front.
1246 for (numap
= 0, x
= t
; x
!= 0; numap
++, x
= x
->next
);
1253 tp
= am(myself
, numap
* sizeof (tp
[0]));
1262 * q == 0 trivially matches any 't' of the correct object type
1264 * wantObj != 0 means we ignore 'q'
1266 if (q
== 0 || wantObj
) {
1267 for (i
= 0, x
= t
, nm
= 0; i
< numap
; i
++, x
= x
->next
) {
1268 if (x
->objectDN
== 0)
1271 if (x
->objectDN
->write
.scope
==
1275 if (x
->objectDN
->read
.scope
==
1280 if (x
->numColumns
> 0)
1283 if (x
->numColumns
<= 0)
1286 if (dbId
!= 0 && x
->dbId
!= 0 &&
1287 strcmp(dbId
, x
->dbId
) != 0)
1300 /* Scan all mappings, and collect candidates */
1301 for (nm
= 0, x
= t
; x
!= 0; x
= x
->next
) {
1302 if (x
->objectDN
== 0)
1305 if (x
->objectDN
->write
.scope
== LDAP_SCOPE_UNKNOWN
)
1308 if (x
->objectDN
->read
.scope
== LDAP_SCOPE_UNKNOWN
)
1311 /* Only want table/entry mappings */
1312 if (x
->numColumns
<= 0)
1314 if (dbId
!= 0 && x
->dbId
!= 0 &&
1315 strcmp(dbId
, x
->dbId
) != 0)
1318 * It's a match if: there are no indexes, or we actually
1319 * match the query with the indexes.
1321 if (x
->index
.numIndexes
<= 0 ||
1322 verifyIndexMatch(x
, q
, 0, 0, 0)) {
1339 * Return 1 if there's an indexed mapping, 0 otherwise.
1342 haveIndexedMapping(__nis_table_mapping_t
*t
) {
1343 __nis_table_mapping_t
*x
;
1345 for (x
= t
; x
!= 0; x
= x
->next
) {
1346 if (x
->index
.numIndexes
> 0)
1354 * Given an input string 'attrs' of the form "attr1=val1,attr2=val2,...",
1355 * or a filter, return the value associated with the attribute 'attrName'.
1356 * If no instance of 'attrName' is found, return 'default'. In all cases,
1357 * the return value is a copy, and must be freed by the caller.
1359 * Of course, return NULL in case of failure.
1362 attrVal(char *msg
, char *attrName
, char *def
, char *attrs
) {
1363 char *val
, *filter
, **fc
= 0;
1365 char *myself
= "attrVal";
1367 if (attrName
== 0 || attrs
== 0)
1375 filter
= makeFilter(attrs
);
1376 if (filter
!= 0 && (fc
= makeFilterComp(filter
, &nfc
)) != 0 &&
1378 for (i
= 0; i
< nfc
; i
++) {
1382 /* Skip if not of attr=value form */
1383 if ((value
= strchr(name
, '=')) == 0)
1389 if (strcasecmp(attrName
, name
) == 0) {
1397 val
= sdup(msg
, T
, val
);
1400 freeFilterComp(fc
, nfc
);
1405 extern bool_t
xdr_nis_object(register XDR
*xdrs
, nis_object
*objp
);
1408 * Copy an XDR:ed version of the NIS+ object 'o' (or the one indicated
1409 * by 't->objName' if 'o' is NULL) to the place indicated by
1410 * 't->objectDN->write'. Return an appropriate LDAP status code.
1413 objToLDAP(__nis_table_mapping_t
*t
, nis_object
*o
, entry_obj
**ea
, int numEa
) {
1414 __nis_table_mapping_t
**tp
;
1415 int stat
, osize
, n
, numMatches
= 0;
1417 __nis_rule_value_t
*rv
;
1419 __nis_single_value_t
*sv
;
1420 char **attrName
, *dn
;
1421 char *myself
= "objToLDAP";
1424 return (LDAP_PARAM_ERROR
);
1426 logmsg(MSG_NOTIMECHECK
,
1427 #ifdef NISDB_LDAP_DEBUG
1431 #endif /* NISDB_LDAP_DEBUG */
1432 "%s: %s", myself
, NIL(t
->objName
));
1434 tp
= selectTableMapping(t
, 0, 1, 1, 0, &numMatches
);
1435 if (tp
== 0 || numMatches
<= 0) {
1437 logmsg(MSG_NOTIMECHECK
,
1438 #ifdef NISDB_LDAP_DEBUG
1442 #endif /* NISDB_LDAP_DEBUG */
1443 "%s: %s (no mapping)", myself
, NIL(t
->objName
));
1444 return (LDAP_SUCCESS
);
1447 for (n
= 0; n
< numMatches
; n
++) {
1453 return (LDAP_OPERATIONS_ERROR
);
1456 buf
= (char *)xdrNisObject(o
, ea
, numEa
, &osize
);
1459 return (LDAP_OPERATIONS_ERROR
);
1463 * Prepare to build a rule-value containing the XDR:ed
1466 rv
= am(myself
, sizeof (*rv
));
1467 sv
= am(myself
, sizeof (*sv
));
1468 val
= am(myself
, sizeof (*val
));
1469 attrName
= am(myself
, sizeof (attrName
[0]));
1471 attrName
[0] = attrVal(myself
, "nisplusObject",
1473 t
->objectDN
->write
.attrs
);
1474 if (rv
== 0 || sv
== 0 || val
== 0 || attrName
== 0 ||
1482 return (LDAP_NO_MEMORY
);
1488 /* 'vt_ber' just means "not a NUL-terminated string" */
1495 rv
->attrName
= attrName
;
1499 * The 'write.base' is the actual DN of the entry (and the
1500 * scope had better be 'base', but we don't check that).
1502 dn
= t
->objectDN
->write
.base
;
1504 stat
= ldapModify(dn
, rv
, t
->objectDN
->write
.attrs
, 1);
1506 freeRuleValue(rv
, 1);
1508 logmsg(MSG_NOTIMECHECK
,
1509 #ifdef NISDB_LDAP_DEBUG
1513 #endif /* NISDB_LDAP_DEBUG */
1514 "%s: %s (%s)", myself
, NIL(t
->objName
), ldap_err2string(stat
));
1516 if (stat
!= LDAP_SUCCESS
)
1527 * Retrieve a copy of the 't->objName' object from LDAP, where it's
1528 * stored in XDR:ed form in the place indicated by 't->objectDN->read'.
1529 * Un-XDR the object, and return a pointer to it in '*obj'; it's the
1530 * responsibility of the caller to free the object when it's no
1533 * Returns an appropriate LDAP status.
1536 objFromLDAP(__nis_table_mapping_t
*t
, nis_object
**obj
,
1537 entry_obj
***eaP
, int *numEaP
) {
1538 __nis_table_mapping_t
**tp
;
1540 __nis_rule_value_t
*rv
;
1541 __nis_ldap_search_t
*ls
;
1542 char *attrs
[2], *filter
, **fc
= 0;
1544 int i
, j
, nfc
, nrv
, blen
, stat
= LDAP_SUCCESS
;
1546 char *myself
= "objFromLDAP";
1549 return (LDAP_PARAM_ERROR
);
1552 * If there's nowhere to store the result, we might as
1553 * well pretend all went well, and return right away.
1556 return (LDAP_SUCCESS
);
1558 /* Prepare for the worst */
1561 logmsg(MSG_NOTIMECHECK
,
1562 #ifdef NISDB_LDAP_DEBUG
1566 #endif /* NISDB_LDAP_DEBUG */
1567 "%s: %s", myself
, NIL(t
->objName
));
1569 tp
= selectTableMapping(t
, 0, 0, 1, 0, &numMatches
);
1570 if (tp
== 0 || numMatches
<= 0) {
1572 logmsg(MSG_NOTIMECHECK
,
1573 #ifdef NISDB_LDAP_DEBUG
1577 #endif /* NISDB_LDAP_DEBUG */
1578 "%s: %s (no mapping)", myself
, NIL(t
->objName
));
1579 return (LDAP_SUCCESS
);
1582 for (n
= 0; n
< numMatches
; n
++) {
1586 filter
= makeFilter(t
->objectDN
->read
.attrs
);
1587 if (filter
== 0 || (fc
= makeFilterComp(filter
, &nfc
)) == 0 ||
1591 freeFilterComp(fc
, nfc
);
1592 return ((t
->objectDN
->read
.attrs
!= 0) ?
1593 LDAP_NO_MEMORY
: LDAP_PARAM_ERROR
);
1595 /* Don't need the filter, just the components */
1599 * Look for a "nisplusObject" attribute, and (if found) copy
1600 * the value to attrs[0]. Also remove the "nisplusObject"
1601 * attribute and value from the filter components.
1603 attrs
[0] = sdup(myself
, T
, "nisplusObject");
1604 if (attrs
[0] == 0) {
1606 freeFilterComp(fc
, nfc
);
1607 return (LDAP_NO_MEMORY
);
1610 for (i
= 0; i
< nfc
; i
++) {
1615 /* Skip if not of attr=value form */
1616 if ((value
= strchr(name
, '=')) == 0)
1619 /* Temporarily overWrite the '=' with a '\0' */
1622 /* Compare with our target attribute name */
1623 compare
= strcasecmp("nisplusObject", name
);
1625 /* Put back the '=' */
1628 /* Is it the name we're looking for ? */
1631 attrs
[0] = sdup(myself
, T
, value
+1);
1632 if (attrs
[0] == 0) {
1634 freeFilterComp(fc
, nfc
);
1635 return (LDAP_NO_MEMORY
);
1639 (void) memmove(&fc
[i
], &fc
[i
+1],
1640 (nfc
-1-i
) * sizeof (fc
[i
]));
1646 ls
= buildLdapSearch(t
->objectDN
->read
.base
,
1647 t
->objectDN
->read
.scope
,
1648 nfc
, fc
, 0, attrs
, 0, 1);
1650 freeFilterComp(fc
, nfc
);
1653 return (LDAP_OPERATIONS_ERROR
);
1657 rv
= ldapSearch(ls
, &nrv
, 0, &stat
);
1664 for (i
= 0, buf
= 0; i
< nrv
&& buf
== 0; i
++) {
1665 for (j
= 0; j
< rv
[i
].numAttrs
; j
++) {
1666 if (strcasecmp(ls
->attrs
[0],
1667 rv
[i
].attrName
[j
]) == 0) {
1668 if (rv
[i
].attrVal
[j
].numVals
<= 0)
1670 buf
= rv
[i
].attrVal
[j
].val
[0].value
;
1671 blen
= rv
[i
].attrVal
[j
].val
[0].length
;
1678 o
= unXdrNisObject(buf
, blen
, eaP
, numEaP
);
1682 freeRuleValue(rv
, nrv
);
1683 return (LDAP_OPERATIONS_ERROR
);
1685 stat
= LDAP_SUCCESS
;
1688 stat
= LDAP_NO_SUCH_OBJECT
;
1692 freeRuleValue(rv
, nrv
);
1694 logmsg(MSG_NOTIMECHECK
,
1695 #ifdef NISDB_LDAP_DEBUG
1699 #endif /* NISDB_LDAP_DEBUG */
1700 "%s: %s (%s)", myself
, NIL(t
->objName
), ldap_err2string(stat
));
1702 if (stat
!= LDAP_SUCCESS
)
1713 deleteLDAPobj(__nis_table_mapping_t
*t
) {
1714 __nis_table_mapping_t
**tp
;
1715 int n
, stat
, numMatches
= 0;
1716 char *myself
= "deleteLDAPobj";
1719 return (LDAP_PARAM_ERROR
);
1721 logmsg(MSG_NOTIMECHECK
,
1722 #ifdef NISDB_LDAP_DEBUG
1726 #endif /* NISDB_LDAP_DEBUG */
1727 "%s: %s", myself
, NIL(t
->objName
));
1729 tp
= selectTableMapping(t
, 0, 1, 1, 0, &numMatches
);
1730 if (tp
== 0 || numMatches
<= 0) {
1732 logmsg(MSG_NOTIMECHECK
,
1733 #ifdef NISDB_LDAP_DEBUG
1737 #endif /* NISDB_LDAP_DEBUG */
1738 "%s: %s (no mapping)", myself
, NIL(t
->objName
));
1739 return (LDAP_SUCCESS
);
1742 for (n
= 0; n
< numMatches
; n
++) {
1746 if (t
->objectDN
->delDisp
== dd_always
) {
1747 /* Delete entire entry */
1748 stat
= ldapModify(t
->objectDN
->write
.base
, 0,
1749 t
->objectDN
->write
.attrs
, 1);
1750 } else if (t
->objectDN
->delDisp
== dd_perDbId
) {
1752 * Delete the attribute holding the object.
1753 * First, determine what that attribute is called.
1759 t
->objectDN
->write
.attrs
);
1760 __nis_rule_value_t rv
;
1763 if (attrName
== 0) {
1765 return (LDAP_NO_MEMORY
);
1769 * Build a __nis_value_t with 'numVals' < 0 to
1770 * indicate deletion.
1777 * Build a rule-value with the name we determined
1778 * above, and the deletion value.
1780 (void) memset(&rv
, 0, sizeof (rv
));
1782 rv
.attrName
= &attrName
;
1785 stat
= ldapModify(t
->objectDN
->write
.base
, &rv
,
1786 t
->objectDN
->write
.attrs
, 0);
1789 } else if (t
->objectDN
->delDisp
== dd_never
) {
1790 /* Nothing to do, so we're trivially successful */
1791 stat
= LDAP_SUCCESS
;
1793 stat
= LDAP_PARAM_ERROR
;
1796 logmsg(MSG_NOTIMECHECK
,
1797 #ifdef NISDB_LDAP_DEBUG
1801 #endif /* NISDB_LDAP_DEBUG */
1802 "%s: %s (%s)", myself
, NIL(t
->objName
), ldap_err2string(stat
));
1804 /* If there were no such object, we've trivially succeeded */
1805 if (stat
== LDAP_NO_SUCH_OBJECT
)
1806 stat
= LDAP_SUCCESS
;
1808 if (stat
!= LDAP_SUCCESS
)