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