ccfddbe56edf98530856d2a18d2687b709dfd56e
[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   post process a search result record. For any search_sub[] attributes that were
175   asked for, we need to call the appropriate copy routine to copy the result
176   into the message, then remove any attributes that we added to the search but
177   were not asked for by the user
178 */
179 static int operational_search_post_process(struct ldb_module *module,
180                                            struct ldb_message *msg,
181                                            const char * const *attrs)
182 {
183         struct ldb_context *ldb;
184         int i, a=0;
185
186         ldb = ldb_module_get_ctx(module);
187
188         for (a=0;attrs && attrs[a];a++) {
189                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
190                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
191                                 continue;
192                         }
193
194                         /* construct the new attribute, using either a supplied
195                            constructor or a simple copy */
196                         if (search_sub[i].constructor) {
197                                 if (search_sub[i].constructor(module, msg) != 0) {
198                                         goto failed;
199                                 }
200                         } else if (ldb_msg_copy_attr(msg,
201                                                      search_sub[i].replace,
202                                                      search_sub[i].attr) != 0) {
203                                 goto failed;
204                         }
205
206                         /* remove the added search attribute, unless it was
207                            asked for by the user */
208                         if (search_sub[i].replace == NULL ||
209                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
210                             ldb_attr_in_list(attrs, "*")) {
211                                 continue;
212                         }
213
214                         ldb_msg_remove_attr(msg, search_sub[i].replace);
215                 }
216         }
217
218         return 0;
219
220 failed:
221         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
222                       "operational_search_post_process failed for attribute '%s'",
223                       attrs[a]);
224         return -1;
225 }
226
227
228 /*
229   hook search operations
230 */
231
232 struct operational_context {
233         struct ldb_module *module;
234         struct ldb_request *req;
235
236         const char * const *attrs;
237 };
238
239 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
240 {
241         struct operational_context *ac;
242         int ret;
243
244         ac = talloc_get_type(req->context, struct operational_context);
245
246         if (!ares) {
247                 return ldb_module_done(ac->req, NULL, NULL,
248                                         LDB_ERR_OPERATIONS_ERROR);
249         }
250         if (ares->error != LDB_SUCCESS) {
251                 return ldb_module_done(ac->req, ares->controls,
252                                         ares->response, ares->error);
253         }
254
255         switch (ares->type) {
256         case LDB_REPLY_ENTRY:
257                 /* for each record returned post-process to add any derived
258                    attributes that have been asked for */
259                 ret = operational_search_post_process(ac->module,
260                                                         ares->message,
261                                                         ac->attrs);
262                 if (ret != 0) {
263                         return ldb_module_done(ac->req, NULL, NULL,
264                                                 LDB_ERR_OPERATIONS_ERROR);
265                 }
266                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
267
268         case LDB_REPLY_REFERRAL:
269                 /* ignore referrals */
270                 break;
271
272         case LDB_REPLY_DONE:
273
274                 return ldb_module_done(ac->req, ares->controls,
275                                         ares->response, LDB_SUCCESS);
276         }
277
278         talloc_free(ares);
279         return LDB_SUCCESS;
280 }
281
282 static int operational_search(struct ldb_module *module, struct ldb_request *req)
283 {
284         struct ldb_context *ldb;
285         struct operational_context *ac;
286         struct ldb_request *down_req;
287         const char **search_attrs = NULL;
288         int i, a;
289         int ret;
290
291         ldb = ldb_module_get_ctx(module);
292
293         ac = talloc(req, struct operational_context);
294         if (ac == NULL) {
295                 return LDB_ERR_OPERATIONS_ERROR;
296         }
297
298         ac->module = module;
299         ac->req = req;
300         ac->attrs = req->op.search.attrs;
301
302         /*  FIXME: We must copy the tree and keep the original
303          *  unmodified. SSS */
304         /* replace any attributes in the parse tree that are
305            searchable, but are stored using a different name in the
306            backend */
307         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
308                 ldb_parse_tree_attr_replace(req->op.search.tree,
309                                             parse_tree_sub[i].attr,
310                                             parse_tree_sub[i].replace);
311         }
312
313         /* in the list of attributes we are looking for, rename any
314            attributes to the alias for any hidden attributes that can
315            be fetched directly using non-hidden names */
316         for (a=0;ac->attrs && ac->attrs[a];a++) {
317                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
318                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
319                             search_sub[i].replace) {
320                                 if (!search_attrs) {
321                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
322                                         if (search_attrs == NULL) {
323                                                 return LDB_ERR_OPERATIONS_ERROR;
324                                         }
325                                 }
326                                 search_attrs[a] = search_sub[i].replace;
327                         }
328                 }
329         }
330
331         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
332                                         req->op.search.base,
333                                         req->op.search.scope,
334                                         req->op.search.tree,
335                                         /* use new set of attrs if any */
336                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
337                                         req->controls,
338                                         ac, operational_callback,
339                                         req);
340         if (ret != LDB_SUCCESS) {
341                 return LDB_ERR_OPERATIONS_ERROR;
342         }
343
344         /* perform the search */
345         return ldb_next_request(module, down_req);
346 }
347
348 static int operational_init(struct ldb_module *ctx)
349 {
350         int ret = 0;
351
352         if (ret != 0) {
353                 return ret;
354         }
355
356         return ldb_next_init(ctx);
357 }
358
359 const struct ldb_module_ops ldb_operational_module_ops = {
360         .name              = "operational",
361         .search            = operational_search,
362         .init_context      = operational_init
363 };