23d1a9fe7f6fc2d8156c98d8255dad58da106d2f
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / operational.c
1 /*
2    ldb database library
3
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Simo Sorce 2006-2008
6    Copyright (C) Matthias Dieter Wallnöfer 2009
7
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.
12    
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.
17    
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/>.
20 */
21
22 /*
23   handle operational attributes
24  */
25
26 /*
27   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
28   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
29
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
34
35      we also need to replace these with the whenCreated/whenChanged
36      equivalent in the search expression trees
37
38   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
39   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
40
41      on init we need to setup attribute handlers for these so
42      comparisons are done correctly. The resolution is 1 second.
43
44      on add we need to add both the above, for current time
45
46      on modify we need to change whenChanged
47
48   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
49
50      for this one we do the search as normal, then if requested ask
51      for objectclass, change the attribute name, and add it
52
53   primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
54
55      contains the RID of a certain group object
56     
57
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?
64 */
65
66 #include "includes.h"
67 #include "ldb_includes.h"
68 #include "ldb_module.h"
69
70 #include "librpc/gen_ndr/ndr_misc.h"
71 #include "param/param.h"
72 #include "dsdb/samdb/samdb.h"
73
74 #ifndef ARRAY_SIZE
75 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
76 #endif
77
78 /*
79   construct a canonical name from a message
80 */
81 static int construct_canonical_name(struct ldb_module *module,
82         struct ldb_message *msg)
83 {
84         char *canonicalName;
85         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
86         if (canonicalName == NULL) {
87                 return -1;
88         }
89         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
90 }
91
92 /*
93   construct a primary group token for groups from a message
94 */
95 static int construct_primary_group_token(struct ldb_module *module,
96         struct ldb_message *msg)
97 {
98         struct ldb_context *ldb;
99         uint32_t primary_group_token;
100
101         ldb = ldb_module_get_ctx(module);
102
103         if (samdb_search_count(ldb, ldb, msg->dn, "(objectclass=group)") == 1) {
104                 primary_group_token
105                         = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0);
106                 return samdb_msg_add_int(ldb, ldb, msg, "primaryGroupToken",
107                         primary_group_token);
108         } else {
109                 return LDB_SUCCESS;
110         }
111 }
112
113 static int construct_parent_guid(struct ldb_module *module,
114                 struct ldb_message *msg)
115 {
116         struct ldb_context *ldb;
117         struct GUID parent_guid;
118         int ret;
119
120         ldb = ldb_module_get_ctx(module);
121
122         ret = dsdb_find_parentguid_by_dn(ldb, msg->dn, &parent_guid);
123
124
125         if (ret != LDB_SUCCESS){
126
127                 /* if there is no parentGUID for this object, then return */
128                 if (ret == LDB_ERR_NO_SUCH_OBJECT){
129                         return LDB_SUCCESS;
130                 }else{
131                         return ret;
132                 }
133
134         }
135
136         ret = dsdb_msg_add_guid(msg, &parent_guid, "parentGUID");
137
138         return ret;
139
140 }
141
142
143 /*
144   a list of attribute names that should be substituted in the parse
145   tree before the search is done
146 */
147 static const struct {
148         const char *attr;
149         const char *replace;
150 } parse_tree_sub[] = {
151         { "createTimestamp", "whenCreated" },
152         { "modifyTimestamp", "whenChanged" }
153 };
154
155
156 /*
157   a list of attribute names that are hidden, but can be searched for
158   using another (non-hidden) name to produce the correct result
159 */
160 static const struct {
161         const char *attr;
162         const char *replace;
163         int (*constructor)(struct ldb_module *, struct ldb_message *);
164 } search_sub[] = {
165         { "createTimestamp", "whenCreated", NULL },
166         { "modifyTimestamp", "whenChanged", NULL },
167         { "structuralObjectClass", "objectClass", NULL },
168         { "canonicalName", "distinguishedName", construct_canonical_name },
169         { "primaryGroupToken", "objectSid", construct_primary_group_token },
170         { "parentGUID", NULL, construct_parent_guid }
171 };
172
173
174 enum op_remove {
175         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
176         OPERATIONAL_REMOVE_UNASKED /* remove if not requested */
177 };
178
179 /*
180   a list of attributes that may need to be removed from the
181   underlying db return
182 */
183 static const struct {
184         const char *attr;
185         enum op_remove op;
186 } operational_remove[] = {
187         { "ntSecurityDescriptor", OPERATIONAL_REMOVE_UNASKED },
188         { "parentGUID",           OPERATIONAL_REMOVE_ALWAYS }
189 };
190
191
192 /*
193   post process a search result record. For any search_sub[] attributes that were
194   asked for, we need to call the appropriate copy routine to copy the result
195   into the message, then remove any attributes that we added to the search but
196   were not asked for by the user
197 */
198 static int operational_search_post_process(struct ldb_module *module,
199                                            struct ldb_message *msg,
200                                            const char * const *attrs)
201 {
202         struct ldb_context *ldb;
203         int i, a=0;
204
205         ldb = ldb_module_get_ctx(module);
206
207         /* removed any attrs that should not be shown to the user */
208         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
209                 struct ldb_message_element *el;
210
211                 switch (operational_remove[i].op) {
212                 case OPERATIONAL_REMOVE_UNASKED:
213                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
214                                 continue;
215                         }
216                 case OPERATIONAL_REMOVE_ALWAYS:
217                         el = ldb_msg_find_element(msg, operational_remove[i].attr);
218                         if (el) {
219                                 ldb_msg_remove_element(msg, el);
220                         }
221                         break;
222                 }
223         }
224
225         for (a=0;attrs && attrs[a];a++) {
226                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
227                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
228                                 continue;
229                         }
230
231                         /* construct the new attribute, using either a supplied
232                            constructor or a simple copy */
233                         if (search_sub[i].constructor) {
234                                 if (search_sub[i].constructor(module, msg) != 0) {
235                                         goto failed;
236                                 }
237                         } else if (ldb_msg_copy_attr(msg,
238                                                      search_sub[i].replace,
239                                                      search_sub[i].attr) != 0) {
240                                 goto failed;
241                         }
242
243                         /* remove the added search attribute, unless it was
244                            asked for by the user */
245                         if (search_sub[i].replace == NULL ||
246                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
247                             ldb_attr_in_list(attrs, "*")) {
248                                 continue;
249                         }
250
251                         ldb_msg_remove_attr(msg, search_sub[i].replace);
252                 }
253         }
254
255         return 0;
256
257 failed:
258         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
259                       "operational_search_post_process failed for attribute '%s'",
260                       attrs[a]);
261         return -1;
262 }
263
264
265 /*
266   hook search operations
267 */
268
269 struct operational_context {
270         struct ldb_module *module;
271         struct ldb_request *req;
272
273         const char * const *attrs;
274 };
275
276 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
277 {
278         struct operational_context *ac;
279         int ret;
280
281         ac = talloc_get_type(req->context, struct operational_context);
282
283         if (!ares) {
284                 return ldb_module_done(ac->req, NULL, NULL,
285                                         LDB_ERR_OPERATIONS_ERROR);
286         }
287         if (ares->error != LDB_SUCCESS) {
288                 return ldb_module_done(ac->req, ares->controls,
289                                         ares->response, ares->error);
290         }
291
292         switch (ares->type) {
293         case LDB_REPLY_ENTRY:
294                 /* for each record returned post-process to add any derived
295                    attributes that have been asked for */
296                 ret = operational_search_post_process(ac->module,
297                                                         ares->message,
298                                                         ac->attrs);
299                 if (ret != 0) {
300                         return ldb_module_done(ac->req, NULL, NULL,
301                                                 LDB_ERR_OPERATIONS_ERROR);
302                 }
303                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
304
305         case LDB_REPLY_REFERRAL:
306                 /* ignore referrals */
307                 break;
308
309         case LDB_REPLY_DONE:
310
311                 return ldb_module_done(ac->req, ares->controls,
312                                         ares->response, LDB_SUCCESS);
313         }
314
315         talloc_free(ares);
316         return LDB_SUCCESS;
317 }
318
319 static int operational_search(struct ldb_module *module, struct ldb_request *req)
320 {
321         struct ldb_context *ldb;
322         struct operational_context *ac;
323         struct ldb_request *down_req;
324         const char **search_attrs = NULL;
325         int i, a;
326         int ret;
327
328         ldb = ldb_module_get_ctx(module);
329
330         ac = talloc(req, struct operational_context);
331         if (ac == NULL) {
332                 return LDB_ERR_OPERATIONS_ERROR;
333         }
334
335         ac->module = module;
336         ac->req = req;
337         ac->attrs = req->op.search.attrs;
338
339         /*  FIXME: We must copy the tree and keep the original
340          *  unmodified. SSS */
341         /* replace any attributes in the parse tree that are
342            searchable, but are stored using a different name in the
343            backend */
344         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
345                 ldb_parse_tree_attr_replace(req->op.search.tree,
346                                             parse_tree_sub[i].attr,
347                                             parse_tree_sub[i].replace);
348         }
349
350         /* in the list of attributes we are looking for, rename any
351            attributes to the alias for any hidden attributes that can
352            be fetched directly using non-hidden names */
353         for (a=0;ac->attrs && ac->attrs[a];a++) {
354                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
355                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
356                             search_sub[i].replace) {
357                                 if (!search_attrs) {
358                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
359                                         if (search_attrs == NULL) {
360                                                 return LDB_ERR_OPERATIONS_ERROR;
361                                         }
362                                 }
363                                 search_attrs[a] = search_sub[i].replace;
364                         }
365                 }
366         }
367
368         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
369                                         req->op.search.base,
370                                         req->op.search.scope,
371                                         req->op.search.tree,
372                                         /* use new set of attrs if any */
373                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
374                                         req->controls,
375                                         ac, operational_callback,
376                                         req);
377         if (ret != LDB_SUCCESS) {
378                 return LDB_ERR_OPERATIONS_ERROR;
379         }
380
381         /* perform the search */
382         return ldb_next_request(module, down_req);
383 }
384
385 static int operational_init(struct ldb_module *ctx)
386 {
387         int ret = 0;
388
389         if (ret != 0) {
390                 return ret;
391         }
392
393         return ldb_next_init(ctx);
394 }
395
396 const struct ldb_module_ops ldb_operational_module_ops = {
397         .name              = "operational",
398         .search            = operational_search,
399         .init_context      = operational_init
400 };