/* ldb database library Copyright (C) Stefan Metzmacher 2009 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include "ldb_module.h" #include "dsdb/samdb/samdb.h" static int resolve_oids_need_value(struct ldb_context *ldb, struct dsdb_schema *schema, const struct dsdb_attribute *a, const struct ldb_val *valp) { const struct dsdb_attribute *va = NULL; const struct dsdb_class *vo = NULL; const void *p2; char *str = NULL; if (a->syntax->oMSyntax != 6) { return LDB_ERR_COMPARE_FALSE; } if (valp) { p2 = memchr(valp->data, '.', valp->length); } else { p2 = NULL; } if (!p2) { return LDB_ERR_COMPARE_FALSE; } switch (a->attributeID_id) { case DRSUAPI_ATTID_objectClass: case DRSUAPI_ATTID_subClassOf: case DRSUAPI_ATTID_auxiliaryClass: case DRSUAPI_ATTID_systemPossSuperiors: case DRSUAPI_ATTID_possSuperiors: str = talloc_strndup(ldb, (char *)valp->data, valp->length); if (!str) { return ldb_oom(ldb); } vo = dsdb_class_by_governsID_oid(schema, str); talloc_free(str); if (!vo) { return LDB_ERR_COMPARE_FALSE; } return LDB_ERR_COMPARE_TRUE; case DRSUAPI_ATTID_systemMustContain: case DRSUAPI_ATTID_systemMayContain: case DRSUAPI_ATTID_mustContain: case DRSUAPI_ATTID_mayContain: str = talloc_strndup(ldb, (char *)valp->data, valp->length); if (!str) { return ldb_oom(ldb); } va = dsdb_attribute_by_attributeID_oid(schema, str); talloc_free(str); if (!va) { return LDB_ERR_COMPARE_FALSE; } return LDB_ERR_COMPARE_TRUE; case DRSUAPI_ATTID_governsID: case DRSUAPI_ATTID_attributeID: case DRSUAPI_ATTID_attributeSyntax: return LDB_ERR_COMPARE_FALSE; } return LDB_ERR_COMPARE_FALSE; } static int resolve_oids_parse_tree_need(struct ldb_context *ldb, struct dsdb_schema *schema, const struct ldb_parse_tree *tree) { unsigned int i; const struct dsdb_attribute *a = NULL; const char *attr; const char *p1; const void *p2; const struct ldb_val *valp = NULL; int ret; switch (tree->operation) { case LDB_OP_AND: case LDB_OP_OR: for (i=0;iu.list.num_elements;i++) { ret = resolve_oids_parse_tree_need(ldb, schema, tree->u.list.elements[i]); if (ret != LDB_ERR_COMPARE_FALSE) { return ret; } } return LDB_ERR_COMPARE_FALSE; case LDB_OP_NOT: return resolve_oids_parse_tree_need(ldb, schema, tree->u.isnot.child); case LDB_OP_EQUALITY: case LDB_OP_GREATER: case LDB_OP_LESS: case LDB_OP_APPROX: attr = tree->u.equality.attr; valp = &tree->u.equality.value; break; case LDB_OP_SUBSTRING: attr = tree->u.substring.attr; break; case LDB_OP_PRESENT: attr = tree->u.present.attr; break; case LDB_OP_EXTENDED: attr = tree->u.extended.attr; valp = &tree->u.extended.value; break; default: return LDB_ERR_COMPARE_FALSE; } p1 = strchr(attr, '.'); if (valp) { p2 = memchr(valp->data, '.', valp->length); } else { p2 = NULL; } if (!p1 && !p2) { return LDB_ERR_COMPARE_FALSE; } if (p1) { a = dsdb_attribute_by_attributeID_oid(schema, attr); } else { a = dsdb_attribute_by_lDAPDisplayName(schema, attr); } if (!a) { return LDB_ERR_COMPARE_FALSE; } if (!p2) { return LDB_ERR_COMPARE_FALSE; } return resolve_oids_need_value(ldb, schema, a, valp); } static int resolve_oids_element_need(struct ldb_context *ldb, struct dsdb_schema *schema, const struct ldb_message_element *el) { unsigned int i; const struct dsdb_attribute *a = NULL; const char *p1; p1 = strchr(el->name, '.'); if (p1) { a = dsdb_attribute_by_attributeID_oid(schema, el->name); } else { a = dsdb_attribute_by_lDAPDisplayName(schema, el->name); } if (!a) { return LDB_ERR_COMPARE_FALSE; } for (i=0; i < el->num_values; i++) { int ret; ret = resolve_oids_need_value(ldb, schema, a, &el->values[i]); if (ret != LDB_ERR_COMPARE_FALSE) { return ret; } } return LDB_ERR_COMPARE_FALSE; } static int resolve_oids_message_need(struct ldb_context *ldb, struct dsdb_schema *schema, const struct ldb_message *msg) { unsigned int i; for (i=0; i < msg->num_elements; i++) { int ret; ret = resolve_oids_element_need(ldb, schema, &msg->elements[i]); if (ret != LDB_ERR_COMPARE_FALSE) { return ret; } } return LDB_ERR_COMPARE_FALSE; } static int resolve_oids_replace_value(struct ldb_context *ldb, struct dsdb_schema *schema, const struct dsdb_attribute *a, struct ldb_val *valp) { const struct dsdb_attribute *va = NULL; const struct dsdb_class *vo = NULL; const void *p2; char *str = NULL; if (a->syntax->oMSyntax != 6) { return LDB_SUCCESS; } if (valp) { p2 = memchr(valp->data, '.', valp->length); } else { p2 = NULL; } if (!p2) { return LDB_SUCCESS; } switch (a->attributeID_id) { case DRSUAPI_ATTID_objectClass: case DRSUAPI_ATTID_subClassOf: case DRSUAPI_ATTID_auxiliaryClass: case DRSUAPI_ATTID_systemPossSuperiors: case DRSUAPI_ATTID_possSuperiors: str = talloc_strndup(schema, (char *)valp->data, valp->length); if (!str) { return ldb_oom(ldb); } vo = dsdb_class_by_governsID_oid(schema, str); talloc_free(str); if (!vo) { return LDB_SUCCESS; } *valp = data_blob_string_const(vo->lDAPDisplayName); return LDB_SUCCESS; case DRSUAPI_ATTID_systemMustContain: case DRSUAPI_ATTID_systemMayContain: case DRSUAPI_ATTID_mustContain: case DRSUAPI_ATTID_mayContain: str = talloc_strndup(schema, (char *)valp->data, valp->length); if (!str) { return ldb_oom(ldb); } va = dsdb_attribute_by_attributeID_oid(schema, str); talloc_free(str); if (!va) { return LDB_SUCCESS; } *valp = data_blob_string_const(va->lDAPDisplayName); return LDB_SUCCESS; case DRSUAPI_ATTID_governsID: case DRSUAPI_ATTID_attributeID: case DRSUAPI_ATTID_attributeSyntax: return LDB_SUCCESS; } return LDB_SUCCESS; } static int resolve_oids_parse_tree_replace(struct ldb_context *ldb, struct dsdb_schema *schema, struct ldb_parse_tree *tree) { unsigned int i; const struct dsdb_attribute *a = NULL; const char **attrp; const char *p1; const void *p2; struct ldb_val *valp = NULL; int ret; switch (tree->operation) { case LDB_OP_AND: case LDB_OP_OR: for (i=0;iu.list.num_elements;i++) { ret = resolve_oids_parse_tree_replace(ldb, schema, tree->u.list.elements[i]); if (ret != LDB_SUCCESS) { return ret; } } return LDB_SUCCESS; case LDB_OP_NOT: return resolve_oids_parse_tree_replace(ldb, schema, tree->u.isnot.child); case LDB_OP_EQUALITY: case LDB_OP_GREATER: case LDB_OP_LESS: case LDB_OP_APPROX: attrp = &tree->u.equality.attr; valp = &tree->u.equality.value; break; case LDB_OP_SUBSTRING: attrp = &tree->u.substring.attr; break; case LDB_OP_PRESENT: attrp = &tree->u.present.attr; break; case LDB_OP_EXTENDED: attrp = &tree->u.extended.attr; valp = &tree->u.extended.value; break; default: return LDB_SUCCESS; } p1 = strchr(*attrp, '.'); if (valp) { p2 = memchr(valp->data, '.', valp->length); } else { p2 = NULL; } if (!p1 && !p2) { return LDB_SUCCESS; } if (p1) { a = dsdb_attribute_by_attributeID_oid(schema, *attrp); } else { a = dsdb_attribute_by_lDAPDisplayName(schema, *attrp); } if (!a) { return LDB_SUCCESS; } *attrp = a->lDAPDisplayName; if (!p2) { return LDB_SUCCESS; } if (a->syntax->oMSyntax != 6) { return LDB_SUCCESS; } return resolve_oids_replace_value(ldb, schema, a, valp); } static int resolve_oids_element_replace(struct ldb_context *ldb, struct dsdb_schema *schema, struct ldb_message_element *el) { unsigned int i; const struct dsdb_attribute *a = NULL; const char *p1; p1 = strchr(el->name, '.'); if (p1) { a = dsdb_attribute_by_attributeID_oid(schema, el->name); } else { a = dsdb_attribute_by_lDAPDisplayName(schema, el->name); } if (!a) { return LDB_SUCCESS; } el->name = a->lDAPDisplayName; for (i=0; i < el->num_values; i++) { int ret; ret = resolve_oids_replace_value(ldb, schema, a, &el->values[i]); if (ret != LDB_SUCCESS) { return ret; } } return LDB_SUCCESS; } static int resolve_oids_message_replace(struct ldb_context *ldb, struct dsdb_schema *schema, struct ldb_message *msg) { unsigned int i; for (i=0; i < msg->num_elements; i++) { int ret; ret = resolve_oids_element_replace(ldb, schema, &msg->elements[i]); if (ret != LDB_SUCCESS) { return ret; } } return LDB_SUCCESS; } struct resolve_oids_context { struct ldb_module *module; struct ldb_request *req; }; static int resolve_oids_callback(struct ldb_request *req, struct ldb_reply *ares) { struct resolve_oids_context *ac; ac = talloc_get_type_abort(req->context, struct resolve_oids_context); if (!ares) { return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR); } if (ares->error != LDB_SUCCESS) { return ldb_module_done(ac->req, ares->controls, ares->response, ares->error); } switch (ares->type) { case LDB_REPLY_ENTRY: return ldb_module_send_entry(ac->req, ares->message, ares->controls); case LDB_REPLY_REFERRAL: return ldb_module_send_referral(ac->req, ares->referral); case LDB_REPLY_DONE: return ldb_module_done(ac->req, ares->controls, ares->response, LDB_SUCCESS); } return LDB_SUCCESS; } static int resolve_oids_search(struct ldb_module *module, struct ldb_request *req) { struct ldb_context *ldb; struct dsdb_schema *schema; struct ldb_parse_tree *tree; struct ldb_request *down_req; struct resolve_oids_context *ac; int ret; bool needed = false; const char * const *attrs1; const char **attrs2; unsigned int i; ldb = ldb_module_get_ctx(module); schema = dsdb_get_schema(ldb, NULL); if (!schema) { return ldb_next_request(module, req); } /* do not manipulate our control entries */ if (ldb_dn_is_special(req->op.search.base)) { return ldb_next_request(module, req); } ret = resolve_oids_parse_tree_need(ldb, schema, req->op.search.tree); if (ret == LDB_ERR_COMPARE_TRUE) { needed = true; } else if (ret != LDB_ERR_COMPARE_FALSE) { return ret; } attrs1 = req->op.search.attrs; for (i=0; attrs1 && attrs1[i]; i++) { const char *p; const struct dsdb_attribute *a; p = strchr(attrs1[i], '.'); if (p == NULL) { continue; } a = dsdb_attribute_by_attributeID_oid(schema, attrs1[i]); if (a == NULL) { continue; } needed = true; break; } if (!needed) { return ldb_next_request(module, req); } ac = talloc(req, struct resolve_oids_context); if (ac == NULL) { return ldb_oom(ldb); } ac->module = module; ac->req = req; tree = ldb_parse_tree_copy_shallow(ac, req->op.search.tree); if (!tree) { return ldb_oom(ldb); } schema = talloc_reference(tree, schema); if (!schema) { return ldb_oom(ldb); } ret = resolve_oids_parse_tree_replace(ldb, schema, tree); if (ret != LDB_SUCCESS) { return ret; } attrs2 = str_list_copy_const(ac, discard_const_p(const char *, req->op.search.attrs)); if (req->op.search.attrs && !attrs2) { return ldb_oom(ldb); } for (i=0; attrs2 && attrs2[i]; i++) { const char *p; const struct dsdb_attribute *a; p = strchr(attrs2[i], '.'); if (p == NULL) { continue; } a = dsdb_attribute_by_attributeID_oid(schema, attrs2[i]); if (a == NULL) { continue; } attrs2[i] = a->lDAPDisplayName; } ret = ldb_build_search_req_ex(&down_req, ldb, ac, req->op.search.base, req->op.search.scope, tree, attrs2, req->controls, ac, resolve_oids_callback, req); LDB_REQ_SET_LOCATION(down_req); if (ret != LDB_SUCCESS) { return ret; } /* go on with the call chain */ return ldb_next_request(module, down_req); } static int resolve_oids_add(struct ldb_module *module, struct ldb_request *req) { struct ldb_context *ldb; struct dsdb_schema *schema; int ret; struct ldb_message *msg; struct ldb_request *down_req; struct resolve_oids_context *ac; ldb = ldb_module_get_ctx(module); schema = dsdb_get_schema(ldb, NULL); if (!schema) { return ldb_next_request(module, req); } /* do not manipulate our control entries */ if (ldb_dn_is_special(req->op.add.message->dn)) { return ldb_next_request(module, req); } ret = resolve_oids_message_need(ldb, schema, req->op.add.message); if (ret == LDB_ERR_COMPARE_FALSE) { return ldb_next_request(module, req); } else if (ret != LDB_ERR_COMPARE_TRUE) { return ret; } ac = talloc(req, struct resolve_oids_context); if (ac == NULL) { return ldb_oom(ldb); } ac->module = module; ac->req = req; msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message); if (!msg) { return ldb_oom(ldb); } if (!talloc_reference(msg, schema)) { return ldb_oom(ldb); } ret = resolve_oids_message_replace(ldb, schema, msg); if (ret != LDB_SUCCESS) { return ret; } ret = ldb_build_add_req(&down_req, ldb, ac, msg, req->controls, ac, resolve_oids_callback, req); LDB_REQ_SET_LOCATION(down_req); if (ret != LDB_SUCCESS) { return ret; } /* go on with the call chain */ return ldb_next_request(module, down_req); } static int resolve_oids_modify(struct ldb_module *module, struct ldb_request *req) { struct ldb_context *ldb; struct dsdb_schema *schema; int ret; struct ldb_message *msg; struct ldb_request *down_req; struct resolve_oids_context *ac; ldb = ldb_module_get_ctx(module); schema = dsdb_get_schema(ldb, NULL); if (!schema) { return ldb_next_request(module, req); } /* do not manipulate our control entries */ if (ldb_dn_is_special(req->op.mod.message->dn)) { return ldb_next_request(module, req); } ret = resolve_oids_message_need(ldb, schema, req->op.mod.message); if (ret == LDB_ERR_COMPARE_FALSE) { return ldb_next_request(module, req); } else if (ret != LDB_ERR_COMPARE_TRUE) { return ret; } ac = talloc(req, struct resolve_oids_context); if (ac == NULL) { return ldb_oom(ldb); } ac->module = module; ac->req = req; /* we have to copy the message as the caller might have it as a const */ msg = ldb_msg_copy_shallow(ac, req->op.mod.message); if (msg == NULL) { return ldb_oom(ldb); } if (!talloc_reference(msg, schema)) { return ldb_oom(ldb); } ret = resolve_oids_message_replace(ldb, schema, msg); if (ret != LDB_SUCCESS) { return ret; } ret = ldb_build_mod_req(&down_req, ldb, ac, msg, req->controls, ac, resolve_oids_callback, req); LDB_REQ_SET_LOCATION(down_req); if (ret != LDB_SUCCESS) { return ret; } /* go on with the call chain */ return ldb_next_request(module, down_req); } static const struct ldb_module_ops ldb_resolve_oids_module_ops = { .name = "resolve_oids", .search = resolve_oids_search, .add = resolve_oids_add, .modify = resolve_oids_modify, }; int ldb_resolve_oids_module_init(const char *version) { LDB_MODULE_CHECK_VERSION(version); return ldb_register_module(&ldb_resolve_oids_module_ops); }