+ struct ldb_result *res;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ int ret;
+
+ ret = dsdb_module_search(module, tmp_ctx, &res, NULL, LDB_SCOPE_SUBTREE,
+ NULL,
+ DSDB_FLAG_NEXT_MODULE |
+ DSDB_SEARCH_SEARCH_ALL_PARTITIONS,
+ parent,
+ "(&(objectClass=msDS-OptionalFeature)"
+ "(msDS-OptionalFeatureGUID=%s))",GUID_string(tmp_ctx, &op_feature_guid));
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+ if (res->count == 0) {
+ talloc_free(tmp_ctx);
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ if (res->count != 1) {
+ ldb_asprintf_errstring(ldb,
+ "More than one object found matching optional feature GUID %s\n",
+ GUID_string(tmp_ctx, &op_feature_guid));
+ talloc_free(tmp_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *msg = talloc_steal(mem_ctx, res->msgs[0]);
+
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+}
+
+static int rootdse_enable_recycle_bin(struct ldb_module *module,struct ldb_context *ldb,
+ TALLOC_CTX *mem_ctx, struct ldb_dn *op_feature_scope_dn,
+ struct ldb_message *op_feature_msg, struct ldb_request *parent)
+{
+ int ret;
+ const int domain_func_level = dsdb_functional_level(ldb);
+ struct ldb_dn *ntds_settings_dn;
+ TALLOC_CTX *tmp_ctx;
+ unsigned int el_count = 0;
+ struct ldb_message *msg;
+
+ ret = ldb_msg_find_attr_as_int(op_feature_msg, "msDS-RequiredForestBehaviorVersion", 0);
+ if (domain_func_level < ret){
+ ldb_asprintf_errstring(ldb,
+ "rootdse_enable_recycle_bin: Domain functional level must be at least %d\n",
+ ret);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ tmp_ctx = talloc_new(mem_ctx);
+ ntds_settings_dn = samdb_ntds_settings_dn(ldb);
+ if (!ntds_settings_dn) {
+ DEBUG(0, (__location__ ": Failed to find NTDS settings DN\n"));
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ ntds_settings_dn = ldb_dn_copy(tmp_ctx, ntds_settings_dn);
+ if (!ntds_settings_dn) {
+ DEBUG(0, (__location__ ": Failed to copy NTDS settings DN\n"));
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ msg->dn = ntds_settings_dn;
+
+ ldb_msg_add_linearized_dn(msg, "msDS-EnabledFeature", op_feature_msg->dn);
+ msg->elements[el_count++].flags = LDB_FLAG_MOD_ADD;
+
+ ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb,
+ "rootdse_enable_recycle_bin: Failed to modify object %s - %s",
+ ldb_dn_get_linearized(ntds_settings_dn),
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ msg->dn = op_feature_scope_dn;
+ ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, parent);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb,
+ "rootdse_enable_recycle_bin: Failed to modify object %s - %s",
+ ldb_dn_get_linearized(op_feature_scope_dn),
+ ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int rootdse_enableoptionalfeature(struct ldb_module *module, struct ldb_request *req)
+{
+ /*
+ steps:
+ - check for system (only system can enable features)
+ - extract GUID from the request
+ - find the feature object
+ - check functional level, must be at least msDS-RequiredForestBehaviorVersion
+ - check if it is already enabled (if enabled return LDAP_ATTRIBUTE_OR_VALUE_EXISTS) - probably not needed, just return error from the add/modify
+ - add/modify objects (see ntdsconnection code for an example)
+ */
+
+ struct ldb_context *ldb = ldb_module_get_ctx(module);
+ struct GUID op_feature_guid;
+ struct ldb_dn *op_feature_scope_dn;
+ struct ldb_message *op_feature_msg;
+ struct auth_session_info *session_info =
+ (struct auth_session_info *)ldb_get_opaque(ldb, "sessionInfo");
+ TALLOC_CTX *tmp_ctx = talloc_new(ldb);
+ int ret;
+ const char *guid_string;
+
+ if (security_session_user_level(session_info, NULL) != SECURITY_SYSTEM) {
+ ldb_set_errstring(ldb, "rootdse: Insufficient rights for enableoptionalfeature");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ ret = get_optional_feature_dn_guid(req, ldb, tmp_ctx, &op_feature_scope_dn, &op_feature_guid);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ guid_string = GUID_string(tmp_ctx, &op_feature_guid);
+ if (!guid_string) {
+ ldb_set_errstring(ldb, "rootdse: bad optional feature GUID");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ ret = dsdb_find_optional_feature(module, ldb, tmp_ctx, op_feature_guid, &op_feature_msg, req);
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb,
+ "rootdse: unable to find optional feature for %s - %s",
+ guid_string, ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ if (strcasecmp(DS_GUID_FEATURE_RECYCLE_BIN, guid_string) == 0) {
+ ret = rootdse_enable_recycle_bin(module, ldb,
+ tmp_ctx, op_feature_scope_dn,
+ op_feature_msg, req);
+ } else {
+ ldb_asprintf_errstring(ldb,
+ "rootdse: unknown optional feature %s",
+ guid_string);
+ talloc_free(tmp_ctx);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+ if (ret != LDB_SUCCESS) {
+ ldb_asprintf_errstring(ldb,
+ "rootdse: failed to set optional feature for %s - %s",
+ guid_string, ldb_errstring(ldb));
+ talloc_free(tmp_ctx);
+ return ret;
+ }
+
+ talloc_free(tmp_ctx);
+ return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);;
+}
+
+static int rootdse_schemaupdatenow(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb = ldb_module_get_ctx(module);