84dff527cbdfadd1282813db85cb4756b1c6f8e7
[tprouty/samba.git] / source3 / lib / ldb / modules / operational.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Tridgell 2005
5    Copyright (C) Simo Sorce 2006
6
7      ** NOTE! The following LGPL license applies to the ldb
8      ** library. This does NOT imply that all of Samba is released
9      ** under the LGPL
10    
11    This library is free software; you can redistribute it and/or
12    modify it under the terms of the GNU Lesser General Public
13    License as published by the Free Software Foundation; either
14    version 3 of the License, or (at your option) any later version.
15
16    This library is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    Lesser General Public License for more details.
20
21    You should have received a copy of the GNU Lesser General Public
22    License along with this library; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 */
25 /*
26   handle operational attributes
27  */
28
29 /*
30   createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
31   modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
32
33      for the above two, we do the search as normal, and if
34      createTimestamp or modifyTimestamp is asked for, then do
35      additional searches for whenCreated and whenChanged and fill in
36      the resulting values
37
38      we also need to replace these with the whenCreated/whenChanged
39      equivalent in the search expression trees
40
41   whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
42   whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
43
44      on init we need to setup attribute handlers for these so
45      comparisons are done correctly. The resolution is 1 second.
46
47      on add we need to add both the above, for current time
48
49      on modify we need to change whenChanged
50
51
52   subschemaSubentry: HIDDEN, not-searchable, 
53                      points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
54
55      for this one we do the search as normal, then add the static
56      value if requested. How do we work out the $BASEDN from inside a
57      module?
58      
59
60   structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
61
62      for this one we do the search as normal, then if requested ask
63      for objectclass, change the attribute name, and add it
64
65   allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable, 
66      list of attributes that can be modified - requires schema lookup
67
68
69   attributeTypes: in schema only
70   objectClasses: in schema only
71   matchingRules: in schema only
72   matchingRuleUse: in schema only
73   creatorsName: not supported by w2k3?
74   modifiersName: not supported by w2k3?
75 */
76
77 #include "includes.h"
78 #include "ldb/include/includes.h"
79
80 /*
81   construct a canonical name from a message
82 */
83 static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
84 {
85         char *canonicalName;
86         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
87         if (canonicalName == NULL) {
88                 return -1;
89         }
90         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
91 }
92
93 /*
94   a list of attribute names that should be substituted in the parse
95   tree before the search is done
96 */
97 static const struct {
98         const char *attr;
99         const char *replace;
100 } parse_tree_sub[] = {
101         { "createTimestamp", "whenCreated" },
102         { "modifyTimestamp", "whenChanged" }
103 };
104
105
106 /*
107   a list of attribute names that are hidden, but can be searched for
108   using another (non-hidden) name to produce the correct result
109 */
110 static const struct {
111         const char *attr;
112         const char *replace;
113         int (*constructor)(struct ldb_module *, struct ldb_message *);
114 } search_sub[] = {
115         { "createTimestamp", "whenCreated", NULL },
116         { "modifyTimestamp", "whenChanged", NULL },
117         { "structuralObjectClass", "objectClass", NULL },
118         { "canonicalName", "distinguishedName", construct_canonical_name }
119 };
120
121 /*
122   post process a search result record. For any search_sub[] attributes that were
123   asked for, we need to call the appropriate copy routine to copy the result
124   into the message, then remove any attributes that we added to the search but were
125   not asked for by the user
126 */
127 static int operational_search_post_process(struct ldb_module *module,
128                                            struct ldb_message *msg, 
129                                            const char * const *attrs)
130 {
131         int i, a=0;
132
133         for (a=0;attrs && attrs[a];a++) {
134                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
135                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
136                                 continue;
137                         }
138
139                         /* construct the new attribute, using either a supplied 
140                            constructor or a simple copy */
141                         if (search_sub[i].constructor) {
142                                 if (search_sub[i].constructor(module, msg) != 0) {
143                                         goto failed;
144                                 }
145                         } else if (ldb_msg_copy_attr(msg,
146                                                      search_sub[i].replace,
147                                                      search_sub[i].attr) != 0) {
148                                 goto failed;
149                         }
150
151                         /* remove the added search attribute, unless it was asked for 
152                            by the user */
153                         if (search_sub[i].replace == NULL ||
154                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
155                             ldb_attr_in_list(attrs, "*")) {
156                                 continue;
157                         }
158
159                         ldb_msg_remove_attr(msg, search_sub[i].replace);
160                 }
161         }
162
163         return 0;
164
165 failed:
166         ldb_debug_set(module->ldb, LDB_DEBUG_WARNING, 
167                       "operational_search_post_process failed for attribute '%s'\n", 
168                       attrs[a]);
169         return -1;
170 }
171
172
173 /*
174   hook search operations
175 */
176
177 struct operational_context {
178
179         struct ldb_module *module;
180         void *up_context;
181         int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
182
183         const char * const *attrs;
184 };
185
186 static int operational_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
187 {
188         struct operational_context *ac;
189
190         if (!context || !ares) {
191                 ldb_set_errstring(ldb, "NULL Context or Result in callback");
192                 goto error;
193         }
194
195         ac = talloc_get_type(context, struct operational_context);
196
197         if (ares->type == LDB_REPLY_ENTRY) {
198                 /* for each record returned post-process to add any derived
199                    attributes that have been asked for */
200                 if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
201                         goto error;
202                 }
203         }
204
205         return ac->up_callback(ldb, ac->up_context, ares);
206
207 error:
208         talloc_free(ares);
209         return LDB_ERR_OPERATIONS_ERROR;
210 }
211
212 static int operational_search(struct ldb_module *module, struct ldb_request *req)
213 {
214         struct operational_context *ac;
215         struct ldb_request *down_req;
216         const char **search_attrs = NULL;
217         int i, a, ret;
218
219         req->handle = NULL;
220
221         ac = talloc(req, struct operational_context);
222         if (ac == NULL) {
223                 return LDB_ERR_OPERATIONS_ERROR;
224         }
225
226         ac->module = module;
227         ac->up_context = req->context;
228         ac->up_callback = req->callback;
229         ac->attrs = req->op.search.attrs;
230
231         down_req = talloc_zero(req, struct ldb_request);
232         if (down_req == NULL) {
233                 return LDB_ERR_OPERATIONS_ERROR;
234         }
235
236         down_req->operation = req->operation;
237         down_req->op.search.base = req->op.search.base;
238         down_req->op.search.scope = req->op.search.scope;
239         down_req->op.search.tree = req->op.search.tree;
240
241         /*  FIXME: I hink we should copy the tree and keep the original
242          *  unmodified. SSS */
243         /* replace any attributes in the parse tree that are
244            searchable, but are stored using a different name in the
245            backend */
246         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
247                 ldb_parse_tree_attr_replace(discard_const_p(struct ldb_parse_tree, req->op.search.tree), 
248                                             parse_tree_sub[i].attr, 
249                                             parse_tree_sub[i].replace);
250         }
251
252         /* in the list of attributes we are looking for, rename any
253            attributes to the alias for any hidden attributes that can
254            be fetched directly using non-hidden names */
255         for (a=0;ac->attrs && ac->attrs[a];a++) {
256                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
257                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
258                             search_sub[i].replace) {
259                                 if (!search_attrs) {
260                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
261                                         if (search_attrs == NULL) {
262                                                 return LDB_ERR_OPERATIONS_ERROR;
263                                         }
264                                 }
265                                 search_attrs[a] = search_sub[i].replace;
266                         }
267                 }
268         }
269         
270         /* use new set of attrs if any */
271         if (search_attrs) down_req->op.search.attrs = search_attrs;
272         else down_req->op.search.attrs = req->op.search.attrs;
273         
274         down_req->controls = req->controls;
275
276         down_req->context = ac;
277         down_req->callback = operational_callback;
278         ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
279
280         /* perform the search */
281         ret = ldb_next_request(module, down_req);
282
283         /* do not free down_req as the call results may be linked to it,
284          * it will be freed when the upper level request get freed */
285         if (ret == LDB_SUCCESS) {
286                 req->handle = down_req->handle;
287         }
288
289         return ret;
290 }
291
292 static int operational_init(struct ldb_module *ctx)
293 {
294         /* setup some standard attribute handlers */
295         ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
296         ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
297         ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
298         ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
299
300         return ldb_next_init(ctx);
301 }
302
303 static const struct ldb_module_ops operational_ops = {
304         .name              = "operational",
305         .search            = operational_search,
306         .init_context      = operational_init
307 };
308
309 int ldb_operational_init(void)
310 {
311         return ldb_register_module(&operational_ops);
312 }