s4:operational - Remove some outdated comments
[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      ** NOTE! The following LGPL license applies to the ldb
9      ** library. This does NOT imply that all of Samba is released
10      ** under the LGPL
11
12    This library is free software; you can redistribute it and/or
13    modify it under the terms of the GNU Lesser General Public
14    License as published by the Free Software Foundation; either
15    version 3 of the License, or (at your option) any later version.
16
17    This library is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20    Lesser General Public License for more details.
21
22    You should have received a copy of the GNU Lesser General Public
23    License along with this library; if not, see <http://www.gnu.org/licenses/>.
24 */
25
26 /*
27   handle operational attributes
28  */
29
30 /*
31   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
32   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
33
34      for the above two, we do the search as normal, and if
35      createTimestamp or modifyTimestamp is asked for, then do
36      additional searches for whenCreated and whenChanged and fill in
37      the resulting values
38
39      we also need to replace these with the whenCreated/whenChanged
40      equivalent in the search expression trees
41
42   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
43   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
44
45      on init we need to setup attribute handlers for these so
46      comparisons are done correctly. The resolution is 1 second.
47
48      on add we need to add both the above, for current time
49
50      on modify we need to change whenChanged
51
52   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
53
54      for this one we do the search as normal, then if requested ask
55      for objectclass, change the attribute name, and add it
56
57   primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
58
59      contains the RID of a certain group object
60     
61
62   attributeTypes: in schema only
63   objectClasses: in schema only
64   matchingRules: in schema only
65   matchingRuleUse: in schema only
66   creatorsName: not supported by w2k3?
67   modifiersName: not supported by w2k3?
68 */
69
70 #include "ldb_includes.h"
71 #include "ldb_module.h"
72
73 #include "includes.h"
74 #include "dsdb/samdb/samdb.h"
75
76 #ifndef ARRAY_SIZE
77 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
78 #endif
79
80 /*
81   construct a canonical name from a message
82 */
83 static int construct_canonical_name(struct ldb_module *module,
84         struct ldb_message *msg)
85 {
86         char *canonicalName;
87         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
88         if (canonicalName == NULL) {
89                 return -1;
90         }
91         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
92 }
93
94 /*
95   construct a primary group token for groups from a message
96 */
97 static int construct_primary_group_token(struct ldb_module *module,
98         struct ldb_message *msg)
99 {
100         struct ldb_context *ldb;
101         uint32_t primary_group_token;
102
103         ldb = ldb_module_get_ctx(module);
104
105         if (samdb_search_count(ldb, ldb, msg->dn, "(objectclass=group)") == 1) {
106                 primary_group_token
107                         = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0);
108                 return samdb_msg_add_int(ldb, ldb, msg, "primaryGroupToken",
109                         primary_group_token);
110         } else {
111                 return LDB_SUCCESS;
112         }
113 }
114
115
116 /*
117   a list of attribute names that should be substituted in the parse
118   tree before the search is done
119 */
120 static const struct {
121         const char *attr;
122         const char *replace;
123 } parse_tree_sub[] = {
124         { "createTimestamp", "whenCreated" },
125         { "modifyTimestamp", "whenChanged" }
126 };
127
128
129 /*
130   a list of attribute names that are hidden, but can be searched for
131   using another (non-hidden) name to produce the correct result
132 */
133 static const struct {
134         const char *attr;
135         const char *replace;
136         int (*constructor)(struct ldb_module *, struct ldb_message *);
137 } search_sub[] = {
138         { "createTimestamp", "whenCreated", NULL },
139         { "modifyTimestamp", "whenChanged", NULL },
140         { "structuralObjectClass", "objectClass", NULL },
141         { "canonicalName", "distinguishedName", construct_canonical_name },
142         { "primaryGroupToken", "objectSid", construct_primary_group_token }
143 };
144
145 /*
146   post process a search result record. For any search_sub[] attributes that were
147   asked for, we need to call the appropriate copy routine to copy the result
148   into the message, then remove any attributes that we added to the search but
149   were not asked for by the user
150 */
151 static int operational_search_post_process(struct ldb_module *module,
152                                            struct ldb_message *msg,
153                                            const char * const *attrs)
154 {
155         struct ldb_context *ldb;
156         int i, a=0;
157
158         ldb = ldb_module_get_ctx(module);
159
160         for (a=0;attrs && attrs[a];a++) {
161                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
162                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
163                                 continue;
164                         }
165
166                         /* construct the new attribute, using either a supplied
167                            constructor or a simple copy */
168                         if (search_sub[i].constructor) {
169                                 if (search_sub[i].constructor(module, msg) != 0) {
170                                         goto failed;
171                                 }
172                         } else if (ldb_msg_copy_attr(msg,
173                                                      search_sub[i].replace,
174                                                      search_sub[i].attr) != 0) {
175                                 goto failed;
176                         }
177
178                         /* remove the added search attribute, unless it was
179                            asked for by the user */
180                         if (search_sub[i].replace == NULL ||
181                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
182                             ldb_attr_in_list(attrs, "*")) {
183                                 continue;
184                         }
185
186                         ldb_msg_remove_attr(msg, search_sub[i].replace);
187                 }
188         }
189
190         return 0;
191
192 failed:
193         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
194                       "operational_search_post_process failed for attribute '%s'",
195                       attrs[a]);
196         return -1;
197 }
198
199
200 /*
201   hook search operations
202 */
203
204 struct operational_context {
205         struct ldb_module *module;
206         struct ldb_request *req;
207
208         const char * const *attrs;
209 };
210
211 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
212 {
213         struct operational_context *ac;
214         int ret;
215
216         ac = talloc_get_type(req->context, struct operational_context);
217
218         if (!ares) {
219                 return ldb_module_done(ac->req, NULL, NULL,
220                                         LDB_ERR_OPERATIONS_ERROR);
221         }
222         if (ares->error != LDB_SUCCESS) {
223                 return ldb_module_done(ac->req, ares->controls,
224                                         ares->response, ares->error);
225         }
226
227         switch (ares->type) {
228         case LDB_REPLY_ENTRY:
229                 /* for each record returned post-process to add any derived
230                    attributes that have been asked for */
231                 ret = operational_search_post_process(ac->module,
232                                                         ares->message,
233                                                         ac->attrs);
234                 if (ret != 0) {
235                         return ldb_module_done(ac->req, NULL, NULL,
236                                                 LDB_ERR_OPERATIONS_ERROR);
237                 }
238                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
239
240         case LDB_REPLY_REFERRAL:
241                 /* ignore referrals */
242                 break;
243
244         case LDB_REPLY_DONE:
245
246                 return ldb_module_done(ac->req, ares->controls,
247                                         ares->response, LDB_SUCCESS);
248         }
249
250         talloc_free(ares);
251         return LDB_SUCCESS;
252 }
253
254 static int operational_search(struct ldb_module *module, struct ldb_request *req)
255 {
256         struct ldb_context *ldb;
257         struct operational_context *ac;
258         struct ldb_request *down_req;
259         const char **search_attrs = NULL;
260         int i, a;
261         int ret;
262
263         ldb = ldb_module_get_ctx(module);
264
265         ac = talloc(req, struct operational_context);
266         if (ac == NULL) {
267                 return LDB_ERR_OPERATIONS_ERROR;
268         }
269
270         ac->module = module;
271         ac->req = req;
272         ac->attrs = req->op.search.attrs;
273
274         /*  FIXME: We must copy the tree and keep the original
275          *  unmodified. SSS */
276         /* replace any attributes in the parse tree that are
277            searchable, but are stored using a different name in the
278            backend */
279         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
280                 ldb_parse_tree_attr_replace(req->op.search.tree,
281                                             parse_tree_sub[i].attr,
282                                             parse_tree_sub[i].replace);
283         }
284
285         /* in the list of attributes we are looking for, rename any
286            attributes to the alias for any hidden attributes that can
287            be fetched directly using non-hidden names */
288         for (a=0;ac->attrs && ac->attrs[a];a++) {
289                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
290                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
291                             search_sub[i].replace) {
292                                 if (!search_attrs) {
293                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
294                                         if (search_attrs == NULL) {
295                                                 return LDB_ERR_OPERATIONS_ERROR;
296                                         }
297                                 }
298                                 search_attrs[a] = search_sub[i].replace;
299                         }
300                 }
301         }
302
303         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
304                                         req->op.search.base,
305                                         req->op.search.scope,
306                                         req->op.search.tree,
307                                         /* use new set of attrs if any */
308                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
309                                         req->controls,
310                                         ac, operational_callback,
311                                         req);
312         if (ret != LDB_SUCCESS) {
313                 return LDB_ERR_OPERATIONS_ERROR;
314         }
315
316         /* perform the search */
317         return ldb_next_request(module, down_req);
318 }
319
320 static int operational_init(struct ldb_module *ctx)
321 {
322         int ret = 0;
323
324         if (ret != 0) {
325                 return ret;
326         }
327
328         return ldb_next_init(ctx);
329 }
330
331 const struct ldb_module_ops ldb_operational_module_ops = {
332         .name              = "operational",
333         .search            = operational_search,
334         .init_context      = operational_init
335 };