s4:resolve_oids LDB module - not really a change but a nicer method to call "talloc_r...
[Samba/nascimento.git] / source4 / dsdb / samdb / ldb_modules / operational.c
blob9fc0d1aeaead208954dd823021a4da17a873df22
1 /*
2 ldb database library
4 Copyright (C) Andrew Tridgell 2005
5 Copyright (C) Simo Sorce 2006-2008
6 Copyright (C) Matthias Dieter Wallnöfer 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 handle operational attributes
27 createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
28 modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
30 for the above two, we do the search as normal, and if
31 createTimestamp or modifyTimestamp is asked for, then do
32 additional searches for whenCreated and whenChanged and fill in
33 the resulting values
35 we also need to replace these with the whenCreated/whenChanged
36 equivalent in the search expression trees
38 whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
39 whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
41 on init we need to setup attribute handlers for these so
42 comparisons are done correctly. The resolution is 1 second.
44 on add we need to add both the above, for current time
46 on modify we need to change whenChanged
48 structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
50 for this one we do the search as normal, then if requested ask
51 for objectclass, change the attribute name, and add it
53 primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
55 contains the RID of a certain group object
58 attributeTypes: in schema only
59 objectClasses: in schema only
60 matchingRules: in schema only
61 matchingRuleUse: in schema only
62 creatorsName: not supported by w2k3?
63 modifiersName: not supported by w2k3?
66 #include "includes.h"
67 #include "ldb_includes.h"
68 #include "ldb_module.h"
70 #include "librpc/gen_ndr/ndr_misc.h"
71 #include "param/param.h"
72 #include "dsdb/samdb/samdb.h"
73 #include "dsdb/samdb/ldb_modules/util.h"
75 #include "auth/auth.h"
76 #include "libcli/security/dom_sid.h"
78 #ifndef ARRAY_SIZE
79 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
80 #endif
82 struct operational_data {
83 struct ldb_dn *aggregate_dn;
87 construct a canonical name from a message
89 static int construct_canonical_name(struct ldb_module *module,
90 struct ldb_message *msg)
92 char *canonicalName;
93 canonicalName = ldb_dn_canonical_string(msg, msg->dn);
94 if (canonicalName == NULL) {
95 return LDB_ERR_OPERATIONS_ERROR;
97 return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
101 construct a primary group token for groups from a message
103 static int construct_primary_group_token(struct ldb_module *module,
104 struct ldb_message *msg)
106 struct ldb_context *ldb;
107 uint32_t primary_group_token;
109 ldb = ldb_module_get_ctx(module);
110 if (ldb_match_msg_objectclass(msg, "group") == 1) {
111 primary_group_token
112 = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
113 if (primary_group_token == 0) {
114 return LDB_SUCCESS;
117 return samdb_msg_add_int(ldb, msg, msg, "primaryGroupToken",
118 primary_group_token);
119 } else {
120 return LDB_SUCCESS;
125 construct the token groups for SAM objects from a message
127 static int construct_token_groups(struct ldb_module *module,
128 struct ldb_message *msg)
130 struct ldb_context *ldb;
131 const struct dom_sid *sid;
133 ldb = ldb_module_get_ctx(module);
135 sid = samdb_result_dom_sid(msg, msg, "objectSid");
136 if (sid != NULL) {
137 NTSTATUS status;
138 uint32_t prim_group_rid;
139 struct dom_sid **sids = NULL;
140 unsigned int i, num_sids = 0;
141 int ret;
143 prim_group_rid = samdb_result_uint(msg, "primaryGroupID", 0);
144 if (prim_group_rid != 0) {
145 struct dom_sid *prim_group_sid;
147 prim_group_sid = dom_sid_add_rid(msg,
148 samdb_domain_sid(ldb),
149 prim_group_rid);
150 if (prim_group_sid == NULL) {
151 ldb_oom(ldb);
152 return LDB_ERR_OPERATIONS_ERROR;
155 /* onlyChilds = false, we want to consider also the
156 * "primaryGroupID" for membership */
157 status = authsam_expand_nested_groups(ldb,
158 prim_group_sid,
159 false, msg,
160 &sids, &num_sids);
161 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
162 ldb_oom(ldb);
163 return LDB_ERR_OPERATIONS_ERROR;
165 if (!NT_STATUS_IS_OK(status)) {
166 return LDB_ERR_OPERATIONS_ERROR;
169 for (i = 0; i < num_sids; i++) {
170 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
171 "tokenGroups",
172 sids[i]);
173 if (ret != LDB_SUCCESS) {
174 talloc_free(sids);
175 return ret;
179 talloc_free(sids);
182 sids = NULL;
183 num_sids = 0;
185 /* onlyChils = true, we don't want to have the SAM object itself
186 * in the result */
187 status = authsam_expand_nested_groups(ldb, sid, true, msg,
188 &sids, &num_sids);
189 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
190 ldb_oom(ldb);
191 return LDB_ERR_OPERATIONS_ERROR;
193 if (!NT_STATUS_IS_OK(status)) {
194 return LDB_ERR_OPERATIONS_ERROR;
197 for (i = 0; i < num_sids; i++) {
198 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
199 "tokenGroups", sids[i]);
200 if (ret != LDB_SUCCESS) {
201 talloc_free(sids);
202 return ret;
206 talloc_free(sids);
209 return LDB_SUCCESS;
213 construct the parent GUID for an entry from a message
215 static int construct_parent_guid(struct ldb_module *module,
216 struct ldb_message *msg)
218 struct ldb_result *res;
219 const struct ldb_val *parent_guid;
220 const char *attrs[] = { "objectGUID", NULL };
221 int ret;
222 struct ldb_val v;
224 /* TODO: In the future, this needs to honour the partition boundaries */
225 struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
227 if (parent_dn == NULL) {
228 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
229 ldb_dn_get_linearized(msg->dn)));
230 return LDB_SUCCESS;
233 ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
234 talloc_free(parent_dn);
235 /* if there is no parentGUID for this object, then return */
236 if (ret == LDB_ERR_NO_SUCH_OBJECT) {
237 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
238 ldb_dn_get_linearized(msg->dn)));
239 return LDB_SUCCESS;
240 } else if (ret != LDB_SUCCESS) {
241 return ret;
244 parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
245 if (!parent_guid) {
246 talloc_free(res);
247 return LDB_SUCCESS;
250 v = data_blob_dup_talloc(res, parent_guid);
251 if (!v.data) {
252 talloc_free(res);
253 return LDB_ERR_OPERATIONS_ERROR;
255 ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
256 talloc_free(res);
257 return ret;
261 construct a subSchemaSubEntry
263 static int construct_subschema_subentry(struct ldb_module *module,
264 struct ldb_message *msg)
266 struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
267 char *subSchemaSubEntry;
269 /* We may be being called before the init function has finished */
270 if (!data) {
271 return LDB_SUCCESS;
274 /* Try and set this value up, if possible. Don't worry if it
275 * fails, we may not have the DB set up yet, and it's not
276 * really vital anyway */
277 if (!data->aggregate_dn) {
278 struct ldb_context *ldb = ldb_module_get_ctx(module);
279 data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
282 if (data->aggregate_dn) {
283 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
284 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
286 return LDB_SUCCESS;
291 a list of attribute names that should be substituted in the parse
292 tree before the search is done
294 static const struct {
295 const char *attr;
296 const char *replace;
297 } parse_tree_sub[] = {
298 { "createTimestamp", "whenCreated" },
299 { "modifyTimestamp", "whenChanged" }
304 a list of attribute names that are hidden, but can be searched for
305 using another (non-hidden) name to produce the correct result
307 static const struct {
308 const char *attr;
309 const char *replace;
310 const char *extra_attr;
311 int (*constructor)(struct ldb_module *, struct ldb_message *);
312 } search_sub[] = {
313 { "createTimestamp", "whenCreated", NULL , NULL },
314 { "modifyTimestamp", "whenChanged", NULL , NULL },
315 { "structuralObjectClass", "objectClass", NULL , NULL },
316 { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
317 { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
318 { "tokenGroups", "objectSid", "primaryGroupID", construct_token_groups },
319 { "parentGUID", NULL, NULL, construct_parent_guid },
320 { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry }
324 enum op_remove {
325 OPERATIONAL_REMOVE_ALWAYS, /* remove always */
326 OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
327 OPERATIONAL_SD_FLAGS /* show if SD_FLAGS_OID set, or asked for */
331 a list of attributes that may need to be removed from the
332 underlying db return
334 static const struct {
335 const char *attr;
336 enum op_remove op;
337 } operational_remove[] = {
338 { "nTSecurityDescriptor", OPERATIONAL_SD_FLAGS },
339 { "parentGUID", OPERATIONAL_REMOVE_ALWAYS },
340 { "replPropertyMetaData", OPERATIONAL_REMOVE_UNASKED },
341 { "unicodePwd", OPERATIONAL_REMOVE_UNASKED },
342 { "dBCSPwd", OPERATIONAL_REMOVE_UNASKED },
343 { "ntPwdHistory", OPERATIONAL_REMOVE_UNASKED },
344 { "lmPwdHistory", OPERATIONAL_REMOVE_UNASKED },
345 { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
350 post process a search result record. For any search_sub[] attributes that were
351 asked for, we need to call the appropriate copy routine to copy the result
352 into the message, then remove any attributes that we added to the search but
353 were not asked for by the user
355 static int operational_search_post_process(struct ldb_module *module,
356 struct ldb_message *msg,
357 const char * const *attrs,
358 bool sd_flags_set)
360 struct ldb_context *ldb;
361 unsigned int i, a = 0;
362 bool constructed_attributes = false;
364 ldb = ldb_module_get_ctx(module);
366 /* removed any attrs that should not be shown to the user */
367 for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
368 switch (operational_remove[i].op) {
369 case OPERATIONAL_REMOVE_UNASKED:
370 if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
371 continue;
373 case OPERATIONAL_REMOVE_ALWAYS:
374 ldb_msg_remove_attr(msg, operational_remove[i].attr);
375 break;
376 case OPERATIONAL_SD_FLAGS:
377 if (sd_flags_set ||
378 ldb_attr_in_list(attrs, operational_remove[i].attr)) {
379 continue;
381 ldb_msg_remove_attr(msg, operational_remove[i].attr);
382 break;
386 for (a=0;attrs && attrs[a];a++) {
387 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
388 if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
389 continue;
392 /* construct the new attribute, using either a supplied
393 constructor or a simple copy */
394 constructed_attributes = true;
395 if (search_sub[i].constructor != NULL) {
396 if (search_sub[i].constructor(module, msg) != LDB_SUCCESS) {
397 goto failed;
399 } else if (ldb_msg_copy_attr(msg,
400 search_sub[i].replace,
401 search_sub[i].attr) != LDB_SUCCESS) {
402 goto failed;
407 /* Deletion of the search helper attributes are needed if:
408 * - we generated constructed attributes and
409 * - we aren't requesting all attributes
411 if ((constructed_attributes) && (!ldb_attr_in_list(attrs, "*"))) {
412 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
413 /* remove the added search helper attributes, unless
414 * they were asked for by the user */
415 if (search_sub[i].replace != NULL &&
416 !ldb_attr_in_list(attrs, search_sub[i].replace)) {
417 ldb_msg_remove_attr(msg, search_sub[i].replace);
419 if (search_sub[i].extra_attr != NULL &&
420 !ldb_attr_in_list(attrs, search_sub[i].extra_attr)) {
421 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
426 return 0;
428 failed:
429 ldb_debug_set(ldb, LDB_DEBUG_WARNING,
430 "operational_search_post_process failed for attribute '%s'",
431 attrs[a]);
432 return -1;
437 hook search operations
440 struct operational_context {
441 struct ldb_module *module;
442 struct ldb_request *req;
444 const char * const *attrs;
445 bool sd_flags_set;
448 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
450 struct operational_context *ac;
451 int ret;
453 ac = talloc_get_type(req->context, struct operational_context);
455 if (!ares) {
456 return ldb_module_done(ac->req, NULL, NULL,
457 LDB_ERR_OPERATIONS_ERROR);
459 if (ares->error != LDB_SUCCESS) {
460 return ldb_module_done(ac->req, ares->controls,
461 ares->response, ares->error);
464 switch (ares->type) {
465 case LDB_REPLY_ENTRY:
466 /* for each record returned post-process to add any derived
467 attributes that have been asked for */
468 ret = operational_search_post_process(ac->module,
469 ares->message,
470 ac->attrs,
471 ac->sd_flags_set);
472 if (ret != 0) {
473 return ldb_module_done(ac->req, NULL, NULL,
474 LDB_ERR_OPERATIONS_ERROR);
476 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
478 case LDB_REPLY_REFERRAL:
479 return ldb_module_send_referral(ac->req, ares->referral);
481 case LDB_REPLY_DONE:
483 return ldb_module_done(ac->req, ares->controls,
484 ares->response, LDB_SUCCESS);
487 talloc_free(ares);
488 return LDB_SUCCESS;
491 static int operational_search(struct ldb_module *module, struct ldb_request *req)
493 struct ldb_context *ldb;
494 struct operational_context *ac;
495 struct ldb_request *down_req;
496 const char **search_attrs = NULL;
497 unsigned int i, a;
498 int ret;
500 ldb = ldb_module_get_ctx(module);
502 ac = talloc(req, struct operational_context);
503 if (ac == NULL) {
504 return LDB_ERR_OPERATIONS_ERROR;
507 ac->module = module;
508 ac->req = req;
509 ac->attrs = req->op.search.attrs;
511 /* FIXME: We must copy the tree and keep the original
512 * unmodified. SSS */
513 /* replace any attributes in the parse tree that are
514 searchable, but are stored using a different name in the
515 backend */
516 for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
517 ldb_parse_tree_attr_replace(req->op.search.tree,
518 parse_tree_sub[i].attr,
519 parse_tree_sub[i].replace);
522 /* in the list of attributes we are looking for, rename any
523 attributes to the alias for any hidden attributes that can
524 be fetched directly using non-hidden names */
525 for (a=0;ac->attrs && ac->attrs[a];a++) {
526 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
527 if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
528 search_sub[i].replace) {
530 if (search_sub[i].extra_attr) {
531 const char **search_attrs2;
532 /* Only adds to the end of the list */
533 search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
534 ? search_attrs
535 : ac->attrs,
536 search_sub[i].extra_attr);
537 if (search_attrs2 == NULL) {
538 return LDB_ERR_OPERATIONS_ERROR;
540 /* may be NULL, talloc_free() doesn't mind */
541 talloc_free(search_attrs);
542 search_attrs = search_attrs2;
545 if (!search_attrs) {
546 search_attrs = ldb_attr_list_copy(req, ac->attrs);
547 if (search_attrs == NULL) {
548 return LDB_ERR_OPERATIONS_ERROR;
551 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
552 search_attrs[a] = search_sub[i].replace;
557 /* remember if the SD_FLAGS_OID was set */
558 ac->sd_flags_set = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
560 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
561 req->op.search.base,
562 req->op.search.scope,
563 req->op.search.tree,
564 /* use new set of attrs if any */
565 search_attrs == NULL?req->op.search.attrs:search_attrs,
566 req->controls,
567 ac, operational_callback,
568 req);
569 if (ret != LDB_SUCCESS) {
570 return LDB_ERR_OPERATIONS_ERROR;
573 /* perform the search */
574 return ldb_next_request(module, down_req);
577 static int operational_init(struct ldb_module *ctx)
579 struct operational_data *data;
580 int ret = ldb_next_init(ctx);
582 if (ret != LDB_SUCCESS) {
583 return ret;
586 data = talloc_zero(ctx, struct operational_data);
587 if (!data) {
588 ldb_module_oom(ctx);
589 return LDB_ERR_OPERATIONS_ERROR;
592 ldb_module_set_private(ctx, data);
594 return LDB_SUCCESS;
597 const struct ldb_module_ops ldb_operational_module_ops = {
598 .name = "operational",
599 .search = operational_search,
600 .init_context = operational_init