/* Generate a remote message with a mapped objectClass. */ static void map_objectclass_generate_remote(struct ldb_module *module, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local) { const struct ldb_map_context *data = map_get_context(module); struct ldb_context *ldb; struct ldb_message_element *el, *oc; struct ldb_val val; bool found_extensibleObject = false; unsigned int i; ldb = ldb_module_get_ctx(module); /* Find old local objectClass */ oc = ldb_msg_find_element(old, "objectClass"); if (oc == NULL) { return; } /* Prepare new element */ el = talloc_zero(remote, struct ldb_message_element); if (el == NULL) { ldb_oom(ldb); return; /* TODO: fail? */ } /* Copy local objectClass element, reverse space for an extra value */ el->num_values = oc->num_values + 1; el->values = talloc_array(el, struct ldb_val, el->num_values); if (el->values == NULL) { talloc_free(el); ldb_oom(ldb); return; /* TODO: fail? */ } /* Copy local element name "objectClass" */ el->name = talloc_strdup(el, local_attr); /* Convert all local objectClasses */ for (i = 0; i < el->num_values - 1; i++) { el->values[i] = map_objectclass_convert_local(module, el->values, &oc->values[i]); if (ldb_attr_cmp((char *)el->values[i].data, data->add_objectclass) == 0) { found_extensibleObject = true; } } if (!found_extensibleObject) { val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass); val.length = strlen((char *)val.data); /* Append additional objectClass data->add_objectclass */ el->values[i] = val; } else { el->num_values--; } /* Add new objectClass to remote message */ ldb_msg_add(remote, el, 0); }
/* Check whether a DN is below the local baseDN. */ BOOL ldb_dn_check_local(struct ldb_module *module, const struct ldb_dn *dn) { const struct ldb_map_context *data = map_get_context(module); if (!data->local_base_dn) { return True; } return ldb_dn_compare_base(module->ldb, data->local_base_dn, dn) == 0; }
/* Check whether any data should be stored in the local partition. */ BOOL map_check_local_db(struct ldb_module *module) { const struct ldb_map_context *data = map_get_context(module); if (!data->remote_base_dn || !data->local_base_dn) { return False; } return True; }
/* TODO: This should not be required with GUIDs. */ struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn) { const struct ldb_map_context *data = map_get_context(module); struct ldb_dn *dn1, *dn2; dn1 = ldb_dn_rebase_local(mem_ctx, data, dn); dn2 = ldb_dn_map_remote(module, mem_ctx, dn1); talloc_free(dn1); return dn2; }
/* Generate a local message with a mapped objectClass. */ static struct ldb_message_element *map_objectclass_generate_local(struct ldb_module *module, void *mem_ctx, const char *local_attr, const struct ldb_message *remote) { const struct ldb_map_context *data = map_get_context(module); struct ldb_context *ldb; struct ldb_message_element *el, *oc; struct ldb_val val; unsigned int i; ldb = ldb_module_get_ctx(module); /* Find old remote objectClass */ oc = ldb_msg_find_element(remote, "objectClass"); if (oc == NULL) { return NULL; } /* Prepare new element */ el = talloc_zero(mem_ctx, struct ldb_message_element); if (el == NULL) { ldb_oom(ldb); return NULL; } /* Copy remote objectClass element */ el->num_values = oc->num_values; el->values = talloc_array(el, struct ldb_val, el->num_values); if (el->values == NULL) { talloc_free(el); ldb_oom(ldb); return NULL; } /* Copy remote element name "objectClass" */ el->name = talloc_strdup(el, local_attr); /* Convert all remote objectClasses */ for (i = 0; i < el->num_values; i++) { el->values[i] = map_objectclass_convert_remote(module, el->values, &oc->values[i]); } val.data = (uint8_t *)talloc_strdup(el->values, data->add_objectclass); val.length = strlen((char *)val.data); /* Remove last value if it was the string in data->add_objectclass (eg samba4top, extensibleObject) */ if (ldb_val_equal_exact(&val, &el->values[i-1])) { el->num_values--; el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values); if (el->values == NULL) { talloc_free(el); ldb_oom(ldb); return NULL; } }
/* TODO: free old DNs and messages? */ int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request) { const struct ldb_map_context *data = map_get_context(module); struct ldb_context *ldb; struct ldb_message *msg; ldb = ldb_module_get_ctx(module); switch (request->operation) { case LDB_SEARCH: if (request->op.search.base) { request->op.search.base = ldb_dn_rebase_remote(request, data, request->op.search.base); } else { request->op.search.base = data->remote_base_dn; /* TODO: adjust scope? */ } break; case LDB_ADD: msg = ldb_msg_copy_shallow(request, request->op.add.message); if (msg == NULL) { return LDB_ERR_OPERATIONS_ERROR; } msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn); request->op.add.message = msg; break; case LDB_MODIFY: msg = ldb_msg_copy_shallow(request, request->op.mod.message); if (msg == NULL) { return LDB_ERR_OPERATIONS_ERROR; } msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn); request->op.mod.message = msg; break; case LDB_DELETE: request->op.del.dn = ldb_dn_rebase_remote(request, data, request->op.del.dn); break; case LDB_RENAME: request->op.rename.olddn = ldb_dn_rebase_remote(request, data, request->op.rename.olddn); request->op.rename.newdn = ldb_dn_rebase_remote(request, data, request->op.rename.newdn); break; default: ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_map: " "Invalid remote request!"); return LDB_ERR_OPERATIONS_ERROR; } return ldb_next_request(module, request); }
/* Check whether a message will be (partially) mapped into the remote partition. */ static BOOL ldb_msg_check_remote(struct ldb_module *module, const struct ldb_message *msg) { const struct ldb_map_context *data = map_get_context(module); BOOL ret; int i; for (i = 0; i < msg->num_elements; i++) { ret = map_attr_check_remote(data, msg->elements[i].name); if (ret) { return ret; } } return False; }
/* Map an objectClass into the local partition. */ static struct ldb_val map_objectclass_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val) { const struct ldb_map_context *data = map_get_context(module); const char *name = (char *)val->data; const struct ldb_map_objectclass *map = map_objectclass_find_remote(data, name); struct ldb_val newval; if (map) { newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->local_name); newval.length = strlen((char *)newval.data); return newval; } return ldb_val_dup(mem_ctx, val); }
/* Add a message element either to a local or to a remote message, * depending on whether it goes into the local or remote partition. */ static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg, const char *attr_name, /* const char * const names[], */ const struct ldb_message_element *old) { const struct ldb_map_context *data = map_get_context(module); const struct ldb_map_attribute *map = map_attr_find_local(data, attr_name); struct ldb_message_element *el=NULL; /* Unknown attribute: ignore */ if (map == NULL) { ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: " "Not mapping attribute '%s': no mapping found\n", old->name); goto local; } switch (map->type) { case MAP_IGNORE: goto local; case MAP_CONVERT: if (map->u.convert.convert_local == NULL) { ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: " "Not mapping attribute '%s': " "'convert_local' not set\n", map->local_name); goto local; } /* fall through */ case MAP_KEEP: case MAP_RENAME: el = ldb_msg_el_map_local(module, remote, map, old); break; case MAP_GENERATE: if (map->u.generate.generate_remote == NULL) { ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: " "Not mapping attribute '%s': " "'generate_remote' not set\n", map->local_name); goto local; } /* TODO: if this attr requires context: * make sure all context attrs are mappable (in 'names') * make sure all context attrs have already been mapped? * maybe postpone generation until they have been mapped? */ map->u.generate.generate_remote(module, map->local_name, msg, remote, local); return 0; } if (el == NULL) { return -1; } return ldb_msg_add(remote, el, old->flags); local: el = talloc(local, struct ldb_message_element); if (el == NULL) { map_oom(module); return -1; } *el = *old; /* copy the old element */ return ldb_msg_add(local, el, old->flags); }
/* Map a DN into the local partition. */ struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn) { const struct ldb_map_context *data = map_get_context(module); struct ldb_dn *newdn; const struct ldb_map_attribute *map; enum ldb_map_attr_type map_type; const char *name; struct ldb_val value; int i, ret; if (dn == NULL) { return NULL; } newdn = ldb_dn_copy(mem_ctx, dn); if (newdn == NULL) { map_oom(module); return NULL; } /* For each RDN, map the component name and possibly the value */ for (i = 0; i < ldb_dn_get_comp_num(newdn); i++) { map = map_attr_find_remote(data, ldb_dn_get_component_name(dn, i)); /* Unknown attribute - leave this RDN as is and hope the best... */ if (map == NULL) { map_type = MAP_KEEP; } else { map_type = map->type; } switch (map_type) { case MAP_IGNORE: case MAP_GENERATE: ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: " "MAP_IGNORE/MAP_GENERATE attribute '%s' " "used in DN!\n", ldb_dn_get_component_name(dn, i)); goto failed; case MAP_CONVERT: if (map->u.convert.convert_remote == NULL) { ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: " "'convert_remote' not set for attribute '%s' " "used in DN!\n", ldb_dn_get_component_name(dn, i)); goto failed; } /* fall through */ case MAP_KEEP: case MAP_RENAME: name = map_attr_map_remote(newdn, map, ldb_dn_get_component_name(dn, i)); if (name == NULL) goto failed; value = ldb_val_map_remote(module, newdn, map, ldb_dn_get_component_val(dn, i)); if (value.data == NULL) goto failed; ret = ldb_dn_set_component(newdn, i, name, value); if (ret != LDB_SUCCESS) { goto failed; } break; } } return newdn; failed: talloc_free(newdn); return NULL; }