2 ldb database library - map backend
4 Copyright (C) Jelmer Vernooij 2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "lib/ldb/include/ldb.h"
27 #include "lib/ldb/include/ldb_private.h"
28 #include "lib/ldb/ldb_map/ldb_map.h"
31 * - objectclass hint in ldb_map_attribute
32 * for use when multiple remote attributes (independant of each other)
33 * map to one local attribute. E.g.: (uid, gidNumber) -> unixName
34 * (use MAP_GENERATE instead ?)
37 static const struct ldb_map_attribute builtin_attribute_maps[];
40 struct ldb_map_context context;
41 const char *last_err_string;
45 /* find an attribute by the local name */
46 static const struct ldb_map_attribute *map_find_attr_local(struct ldb_map_context *privdat, const char *attr)
50 for (i = 0; privdat->attribute_maps[i].local_name; i++) {
51 if (!ldb_attr_cmp(privdat->attribute_maps[i].local_name, attr))
52 return &privdat->attribute_maps[i];
58 /* find an attribute by the remote name */
59 static const struct ldb_map_attribute *map_find_attr_remote(struct ldb_map_context *privdat, const char *attr)
63 for (i = 0; privdat->attribute_maps[i].local_name; i++) {
64 if (privdat->attribute_maps[i].type == MAP_IGNORE)
67 if (privdat->attribute_maps[i].type == MAP_GENERATE)
70 if (privdat->attribute_maps[i].type == MAP_KEEP &&
71 ldb_attr_cmp(privdat->attribute_maps[i].local_name, attr) == 0)
72 return &privdat->attribute_maps[i];
74 if ((privdat->attribute_maps[i].type == MAP_RENAME ||
75 privdat->attribute_maps[i].type == MAP_CONVERT) &&
76 ldb_attr_cmp(privdat->attribute_maps[i].u.rename.remote_name, attr) == 0)
77 return &privdat->attribute_maps[i];
84 static struct ldb_parse_tree *ldb_map_parse_tree(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_parse_tree *tree)
87 const struct ldb_map_attribute *attr;
88 struct ldb_parse_tree *new_tree;
89 enum ldb_map_attr_type map_type;
90 struct ldb_val value, newvalue;
91 struct ldb_map_context *privdat = &((struct map_private *)module->private_data)->context;
97 /* Find attr in question and:
98 * - if it has a convert_operator function, run that
99 * - otherwise, replace attr name with required[0] */
101 if (tree->operation == LDB_OP_AND ||
102 tree->operation == LDB_OP_OR) {
104 new_tree = talloc_memdup(ctx, tree, sizeof(*tree));
105 new_tree->u.list.elements = talloc_array(new_tree, struct ldb_parse_tree *, tree->u.list.num_elements);
106 for (i = 0; i < new_tree->u.list.num_elements; i++) {
107 new_tree->u.list.elements[i] = ldb_map_parse_tree(module, new_tree, tree->u.list.elements[i]);
113 if (tree->operation == LDB_OP_NOT) {
114 new_tree = talloc_memdup(ctx, tree, sizeof(*tree));
115 new_tree->u.isnot.child = ldb_map_parse_tree(module, new_tree, tree->u.isnot.child);
119 /* tree->operation is LDB_OP_EQUALITY, LDB_OP_SUBSTRING, LDB_OP_GREATER,
120 * LDB_OP_LESS, LDB_OP_APPROX, LDB_OP_PRESENT or LDB_OP_EXTENDED
122 * (all have attr as the first element)
125 attr = map_find_attr_local(privdat, tree->u.equality.attr);
128 DEBUG(0, ("Unable to find local attribute '%s', leaving as is\n", tree->u.equality.attr));
131 map_type = attr->type;
134 if (attr && attr->convert_operator) {
135 /* Run convert_operator */
136 return attr->convert_operator(privdat, module, tree);
139 if (map_type == MAP_IGNORE)
142 if (map_type == MAP_GENERATE) {
143 DEBUG(0, ("Can't do conversion for MAP_GENERATE in map_parse_tree without convert_operator for '%s'\n", tree->u.equality.attr));
147 if (tree->operation == LDB_OP_EQUALITY) {
148 value = tree->u.equality.value;
149 } else if (tree->operation == LDB_OP_LESS || tree->operation == LDB_OP_GREATER ||
150 tree->operation == LDB_OP_APPROX) {
151 value = tree->u.comparison.value;
152 } else if (tree->operation == LDB_OP_EXTENDED) {
153 value = tree->u.extended.value;
156 new_tree = talloc_memdup(ctx, tree, sizeof(*tree));
158 if (map_type == MAP_KEEP) {
159 new_tree->u.equality.attr = talloc_strdup(new_tree, tree->u.equality.attr);
160 } else { /* MAP_RENAME / MAP_CONVERT */
161 new_tree->u.equality.attr = talloc_strdup(new_tree, attr->u.rename.remote_name);
164 if (new_tree->operation == LDB_OP_PRESENT)
167 if (new_tree->operation == LDB_OP_SUBSTRING) {
168 new_tree->u.substring.chunks = NULL; /* FIXME! */
172 if (map_type == MAP_CONVERT) {
173 newvalue = attr->u.convert.convert_local(privdat, new_tree, &value);
175 newvalue = ldb_val_dup(new_tree, &value);
178 if (new_tree->operation == LDB_OP_EQUALITY) {
179 new_tree->u.equality.value = newvalue;
180 } else if (new_tree->operation == LDB_OP_LESS || new_tree->operation == LDB_OP_GREATER ||
181 new_tree->operation == LDB_OP_APPROX) {
182 new_tree->u.comparison.value = newvalue;
183 } else if (new_tree->operation == LDB_OP_EXTENDED) {
184 new_tree->u.extended.value = newvalue;
185 new_tree->u.extended.rule_id = talloc_strdup(new_tree, tree->u.extended.rule_id);
191 /* Remote DN -> Local DN */
192 static struct ldb_dn *map_remote_dn(struct ldb_map_context *privdat, TALLOC_CTX *ctx, const struct ldb_dn *dn)
194 struct ldb_dn *newdn;
200 newdn = talloc_memdup(ctx, dn, sizeof(*dn));
204 newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
206 if (!newdn->components)
209 /* For each rdn, map the attribute name and possibly the
212 for (i = 0; i < dn->comp_num; i++) {
213 const struct ldb_map_attribute *attr = map_find_attr_remote(privdat, dn->components[i].name);
214 enum ldb_map_attr_type map_type;
216 /* Unknown attribute - leave this dn as is and hope the best... */
217 if (!attr) map_type = MAP_KEEP;
218 else map_type = attr->type;
223 DEBUG(0, ("Local MAP_IGNORE or MAP_GENERATE attribute '%s' used in DN!", dn->components[i].name));
228 newdn->components[i].name = talloc_strdup(newdn->components, dn->components[i].name);
229 newdn->components[i].value = ldb_val_dup(newdn->components, &dn->components[i].value);
233 newdn->components[i].name = talloc_strdup(newdn->components, attr->local_name);
234 newdn->components[i].value = attr->u.convert.convert_remote(privdat, ctx, &dn->components[i].value);
238 newdn->components[i].name = talloc_strdup(newdn->components, attr->local_name);
239 newdn->components[i].value = ldb_val_dup(newdn->components, &dn->components[i].value);
246 /* Local DN -> Remote DN */
247 static struct ldb_dn *map_local_dn(struct ldb_map_context *privdat, TALLOC_CTX *ctx, const struct ldb_dn *dn)
249 struct ldb_dn *newdn;
255 newdn = talloc_memdup(ctx, dn, sizeof(*dn));
259 newdn->components = talloc_array(newdn, struct ldb_dn_component, newdn->comp_num);
261 if (!newdn->components)
264 /* For each rdn, map the attribute name and possibly the
265 * complete rdn using an equality convert_operator call */
267 for (i = 0; i < dn->comp_num; i++) {
268 const struct ldb_map_attribute *attr = map_find_attr_local(privdat, dn->components[i].name);
269 enum ldb_map_attr_type map_type;
271 /* Unknown attribute - leave this dn as is and hope the best... */
272 if (!attr) map_type = MAP_KEEP; else map_type = attr->type;
278 DEBUG(0, ("Local MAP_IGNORE/MAP_GENERATE attribute '%s' used in DN!", dn->components[i].name));
283 newdn->components[i].name = talloc_strdup(newdn->components, attr->u.convert.remote_name);
284 newdn->components[i].value = attr->u.convert.convert_local(privdat, newdn->components, &dn->components[i].value);
288 newdn->components[i].name = talloc_strdup(newdn->components, attr->u.rename.remote_name);
289 newdn->components[i].value = ldb_val_dup(newdn->components, &dn->components[i].value);
293 newdn->components[i].name = talloc_strdup(newdn->components, dn->components[i].name);
294 newdn->components[i].value = ldb_val_dup(newdn->components, &dn->components[i].value);
302 /* Loop over ldb_map_attribute array and add remote_names */
303 static const char **ldb_map_attrs(struct ldb_module *module, const char *const attrs[])
307 int ar_size = 0, last_element = 0;
308 struct ldb_map_context *privdat = &((struct map_private *)module->private_data)->context;
313 /* Start with good guess of number of elements */
314 for (i = 0; attrs[i]; i++);
316 ret = talloc_array(module, const char *, i);
319 for (i = 0; attrs[i]; i++) {
321 const struct ldb_map_attribute *attr = map_find_attr_local(privdat, attrs[i]);
322 enum ldb_map_attr_type map_type;
325 DEBUG(0, ("Local attribute '%s' does not have a definition!\n", attrs[i]));
326 map_type = MAP_IGNORE;
327 } else map_type = attr->type;
331 case MAP_IGNORE: break;
333 if (last_element >= ar_size) {
334 ret = talloc_realloc(module, ret, const char *, ar_size+1);
337 ret[last_element] = attr->local_name;
343 if (last_element >= ar_size) {
344 ret = talloc_realloc(module, ret, const char *, ar_size+1);
347 ret[last_element] = attr->u.rename.remote_name;
352 /* Add remote_names[] for this attribute to the list of
353 * attributes to request from the remote server */
354 for (j = 0; attr->u.generate.remote_names[j]; j++) {
355 if (last_element >= ar_size) {
356 ret = talloc_realloc(module, ret, const char *, ar_size+1);
359 ret[last_element] = attr->u.generate.remote_names[j];
366 if (last_element >= ar_size) {
367 ret = talloc_realloc(module, ret, const char *, ar_size+1);
371 ret[last_element] = NULL;
376 static const char **available_local_attributes(struct ldb_module *module, const struct ldb_message *msg)
378 struct ldb_map_context *privdat = &((struct map_private *)module->private_data)->context;
381 const char **ret = talloc_array(module, const char *, 1);
385 for (i = 0; privdat->attribute_maps[i].local_name; i++) {
387 const struct ldb_map_attribute *attr = &privdat->attribute_maps[i];
389 /* If all remote attributes for this attribute are present, add the
390 * local one to the list */
392 switch (attr->type) {
393 case MAP_IGNORE: break;
395 avail = (ldb_msg_find_ldb_val(msg, attr->local_name) != NULL);
400 avail = (ldb_msg_find_ldb_val(msg, attr->u.rename.remote_name) != NULL);
405 for (j = 0; attr->u.generate.remote_names[j]; j++) {
406 avail &= (ldb_msg_find_ldb_val(msg, attr->u.generate.remote_names[j]) != NULL);
414 ret = talloc_realloc(module, ret, const char *, count+2);
415 ret[count] = attr->local_name;
423 /* Used for search */
424 static struct ldb_message *ldb_map_message_incoming(struct ldb_module *module, const char * const*attrs, const struct ldb_message *mi)
427 struct ldb_message *msg = talloc_zero(module, struct ldb_message);
428 struct ldb_message_element *elm, *oldelm;
429 struct ldb_map_context *privdat = &((struct map_private *)module->private_data)->context;
430 const char **newattrs = NULL;
432 msg->dn = map_remote_dn(privdat, module, mi->dn);
434 /* Loop over attrs, find in ldb_map_attribute array and
438 /* Generate list of the local attributes that /can/ be generated
439 * using the specific remote attributes */
441 attrs = newattrs = available_local_attributes(module, mi);
444 for (i = 0; attrs[i]; i++) {
445 const struct ldb_map_attribute *attr = map_find_attr_local(privdat, attrs[i]);
446 enum ldb_map_attr_type map_type;
449 DEBUG(0, ("Unable to find local attribute '%s' when generating incoming message\n", attrs[i]));
450 map_type = MAP_IGNORE;
451 } else map_type = attr->type;
454 case MAP_IGNORE:break;
456 oldelm = ldb_msg_find_element(mi, attr->u.rename.remote_name);
457 if (!oldelm) continue;
459 elm = talloc(msg, struct ldb_message_element);
460 elm->name = talloc_strdup(elm, attr->local_name);
461 elm->num_values = oldelm->num_values;
462 elm->values = talloc_reference(elm, oldelm->values);
464 ldb_msg_add(module->ldb, msg, elm, oldelm->flags);
468 oldelm = ldb_msg_find_element(mi, attr->u.rename.remote_name);
469 if (!oldelm) continue;
471 elm = talloc(msg, struct ldb_message_element);
472 elm->name = talloc_strdup(elm, attr->local_name);
473 elm->num_values = oldelm->num_values;
474 elm->values = talloc_array(elm, struct ldb_val, elm->num_values);
476 for (j = 0; j < oldelm->num_values; j++)
477 elm->values[j] = attr->u.convert.convert_remote(privdat, elm, &oldelm->values[j]);
479 ldb_msg_add(module->ldb, msg, elm, oldelm->flags);
483 oldelm = ldb_msg_find_element(mi, attr->local_name);
484 if (!oldelm) continue;
486 elm = talloc(msg, struct ldb_message_element);
488 elm->num_values = oldelm->num_values;
489 elm->values = talloc_reference(elm, oldelm->values);
490 elm->name = talloc_strdup(elm, oldelm->name);
492 ldb_msg_add(module->ldb, msg, elm, oldelm->flags);
496 elm = attr->u.generate.generate_local(privdat, msg, attr->local_name, mi);
499 ldb_msg_add(module->ldb, msg, elm, elm->flags);
502 DEBUG(0, ("Unknown attr->type for %s", attr->local_name));
507 talloc_free(newattrs);
512 /* Used for add, modify */
513 static struct ldb_message *ldb_map_message_outgoing(struct ldb_module *module, const struct ldb_message *mo)
515 struct ldb_map_context *privdat = &((struct map_private *)module->private_data)->context;
516 struct ldb_message *msg = talloc_zero(module, struct ldb_message);
517 struct ldb_message_element *elm;
520 msg->private_data = mo->private_data;
522 msg->dn = map_local_dn(privdat, module, mo->dn);
524 /* Loop over mi and call generate_remote for each attribute */
525 for (i = 0; i < mo->num_elements; i++) {
526 const struct ldb_map_attribute *attr = map_find_attr_local(privdat, mo->elements[i].name);
527 enum ldb_map_attr_type map_type;
530 DEBUG(0, ("Undefined local attribute '%s', ignoring\n", mo->elements[i].name));
531 map_type = MAP_IGNORE;
533 } else map_type = attr->type;
536 case MAP_IGNORE: break;
538 elm = talloc(msg, struct ldb_message_element);
540 elm->name = talloc_strdup(elm, attr->u.rename.remote_name);
541 elm->num_values = mo->elements[i].num_values;
542 elm->values = talloc_reference(elm, mo->elements[i].values);
544 ldb_msg_add(module->ldb, msg, elm, mo->elements[i].flags);
548 elm = talloc(msg, struct ldb_message_element);
550 elm->name = talloc_strdup(elm, attr->u.rename.remote_name);
551 elm->num_values = mo->elements[i].num_values;
552 elm->values = talloc_array(elm, struct ldb_val, elm->num_values);
554 for (j = 0; j < elm->num_values; j++) {
555 elm->values[j] = attr->u.convert.convert_local(privdat, msg, &mo->elements[i].values[j]);
558 ldb_msg_add(module->ldb, msg, elm, mo->elements[i].flags);
562 elm = talloc(msg, struct ldb_message_element);
564 elm->num_values = mo->elements[i].num_values;
565 elm->values = talloc_reference(elm, mo->elements[i].values);
566 elm->name = talloc_strdup(elm, mo->elements[i].name);
568 ldb_msg_add(module->ldb, msg, elm, mo->elements[i].flags);
572 attr->u.generate.generate_remote(privdat, attr->local_name, mo, msg);
583 static int map_rename(struct ldb_module *module, const struct ldb_dn *olddn, const struct ldb_dn *newdn)
585 struct ldb_map_context *privdat = &((struct map_private *)module->private_data)->context;
586 struct ldb_dn *n_olddn, *n_newdn;
589 n_olddn = map_local_dn(privdat, module, olddn);
590 n_newdn = map_local_dn(privdat, module, newdn);
592 ret = ldb_next_rename_record(module, n_olddn, n_newdn);
594 talloc_free(n_olddn);
595 talloc_free(n_newdn);
603 static int map_delete(struct ldb_module *module, const struct ldb_dn *dn)
605 struct ldb_map_context *privdat = &((struct map_private *)module->private_data)->context;
606 struct ldb_dn *newdn;
609 newdn = map_local_dn(privdat, module, dn);
611 ret = ldb_next_delete_record(module, newdn);
619 search for matching records using a ldb_parse_tree
621 static int map_search_bytree(struct ldb_module *module, const struct ldb_dn *base,
622 enum ldb_scope scope, struct ldb_parse_tree *tree,
623 const char * const *attrs, struct ldb_message ***res)
626 const char **newattrs;
627 struct ldb_parse_tree *new_tree;
628 struct ldb_dn *new_base;
629 struct ldb_message **newres;
630 struct ldb_map_context *privdat = &((struct map_private *)module->private_data)->context;
633 new_tree = ldb_map_parse_tree(module, module, tree);
634 newattrs = ldb_map_attrs(module, attrs);
635 new_base = map_local_dn(privdat, module, base);
637 ret = ldb_next_search_bytree(module, new_base, scope, new_tree, newattrs, &newres);
639 talloc_free(new_base);
640 talloc_free(new_tree);
641 talloc_free(newattrs);
643 *res = talloc_array(module, struct ldb_message *, ret);
645 for (i = 0; i < ret; i++) {
646 (*res)[i] = ldb_map_message_incoming(module, attrs, newres[i]);
647 talloc_free(newres[i]);
653 search for matching records
655 static int map_search(struct ldb_module *module, const struct ldb_dn *base,
656 enum ldb_scope scope, const char *expression,
657 const char * const *attrs, struct ldb_message ***res)
659 struct map_private *map = module->private_data;
660 struct ldb_parse_tree *tree;
663 tree = ldb_parse_tree(NULL, expression);
665 map->last_err_string = "expression parse failed";
669 ret = map_search_bytree(module, base, scope, tree, attrs, res);
677 static int map_add(struct ldb_module *module, const struct ldb_message *msg)
679 struct ldb_message *nmsg = ldb_map_message_outgoing(module, msg);
682 ret = ldb_next_add_record(module, nmsg);
693 static int map_modify(struct ldb_module *module, const struct ldb_message *msg)
695 struct ldb_message *nmsg = ldb_map_message_outgoing(module, msg);
698 ret = ldb_next_modify_record(module, nmsg);
705 static int map_lock(struct ldb_module *module, const char *lockname)
707 return ldb_next_named_lock(module, lockname);
710 static int map_unlock(struct ldb_module *module, const char *lockname)
712 return ldb_next_named_unlock(module, lockname);
716 return extended error information
718 static const char *map_errstring(struct ldb_module *module)
720 struct map_private *map = module->private_data;
722 if (map->last_err_string)
723 return map->last_err_string;
725 return ldb_next_errstring(module);
728 static const struct ldb_module_ops map_ops = {
730 .search = map_search,
731 .search_bytree = map_search_bytree,
732 .add_record = map_add,
733 .modify_record = map_modify,
734 .delete_record = map_delete,
735 .rename_record = map_rename,
736 .named_lock = map_lock,
737 .named_unlock = map_unlock,
738 .errstring = map_errstring
741 /* the init function */
742 struct ldb_module *ldb_map_init(struct ldb_context *ldb, const struct ldb_map_attribute *attrs, const struct ldb_map_objectclass *ocls, const char *options[])
745 struct ldb_module *ctx;
746 struct map_private *data;
748 ctx = talloc(ldb, struct ldb_module);
752 data = talloc(ctx, struct map_private);
758 data->last_err_string = NULL;
760 /* Get list of attribute maps */
762 data->context.attribute_maps = NULL;
764 for (i = 0; attrs[i].local_name; i++) {
765 data->context.attribute_maps = talloc_realloc(data, data->context.attribute_maps, struct ldb_map_attribute, j+1);
766 data->context.attribute_maps[j] = attrs[i];
770 for (i = 0; builtin_attribute_maps[i].local_name; i++) {
771 data->context.attribute_maps = talloc_realloc(data, data->context.attribute_maps, struct ldb_map_attribute, j+1);
772 data->context.attribute_maps[j] = builtin_attribute_maps[i];
776 data->context.attribute_maps = talloc_realloc(data, data->context.attribute_maps, struct ldb_map_attribute, j+1);
777 ZERO_STRUCT(data->context.attribute_maps[j].local_name);
779 data->context.objectclass_maps = ocls;
780 ctx->private_data = data;
782 ctx->prev = ctx->next = NULL;
788 static struct ldb_val map_convert_local_dn(struct ldb_map_context *map, TALLOC_CTX *ctx, const struct ldb_val *val)
790 struct ldb_dn *dn, *newdn;;
791 struct ldb_val *newval;
793 dn = ldb_dn_explode(ctx, (char *)val->data);
795 newdn = map_local_dn(map, ctx, dn);
799 newval = talloc(ctx, struct ldb_val);
800 newval->data = (uint8_t *)ldb_dn_linearize(ctx, newdn);
801 newval->length = strlen((char *)newval->data);
808 static struct ldb_val map_convert_remote_dn(struct ldb_map_context *map, TALLOC_CTX *ctx, const struct ldb_val *val)
810 struct ldb_dn *dn, *newdn;;
811 struct ldb_val *newval;
813 dn = ldb_dn_explode(ctx, (char *)val->data);
815 newdn = map_remote_dn(map, ctx, dn);
819 newval = talloc(ctx, struct ldb_val);
820 newval->data = (uint8_t *)ldb_dn_linearize(ctx, newdn);
821 newval->length = strlen((char *)newval->data);
828 static struct ldb_val map_convert_local_objectclass(struct ldb_map_context *map, TALLOC_CTX *ctx, const struct ldb_val *val)
832 for (i = 0; map->objectclass_maps[i].local_name; i++) {
833 if (!strcmp(map->objectclass_maps[i].local_name, (char *)val->data)) {
834 struct ldb_val newval;
835 newval.data = (uint8_t*)talloc_strdup(ctx, map->objectclass_maps[i].remote_name);
836 newval.length = strlen((char *)newval.data);
838 return ldb_val_dup(ctx, &newval);
842 DEBUG(1, ("Unable to map local object class '%s'\n", (char *)val->data));
843 return ldb_val_dup(ctx, val);
846 static struct ldb_val map_convert_remote_objectclass(struct ldb_map_context *map, TALLOC_CTX *ctx, const struct ldb_val *val)
850 for (i = 0; map->objectclass_maps[i].remote_name; i++) {
851 if (!strcmp(map->objectclass_maps[i].remote_name, (char *)val->data)) {
852 struct ldb_val newval;
853 newval.data = (uint8_t*)talloc_strdup(ctx, map->objectclass_maps[i].local_name);
854 newval.length = strlen((char *)newval.data);
856 return ldb_val_dup(ctx, &newval);
860 DEBUG(1, ("Unable to map remote object class '%s'\n", (char *)val->data));
861 return ldb_val_dup(ctx, val);
864 static const struct ldb_map_attribute builtin_attribute_maps[] = {
868 .u.convert.remote_name = "dn",
869 .u.convert.convert_local = map_convert_local_dn,
870 .u.convert.convert_remote = map_convert_remote_dn,
873 .local_name = "objectclass",
875 .u.convert.remote_name = "objectclass",
876 .u.convert.convert_local = map_convert_local_objectclass,
877 .u.convert.convert_remote = map_convert_remote_objectclass,