9cbe1db0703371e6f73eee3fdacbd60e426092ea
[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
53   subschemaSubentry: HIDDEN, not-searchable,
54                      points at DN CN=Aggregate,$SCHEMADN
55
56      for this one we do the search as normal, then add the static
57      value if requested. How do we work out the $BASEDN from inside a
58      module?
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
67      list of attributes that can be modified - requires schema lookup
68
69   primaryGroupToken: HIDDEN, CONSTRUCTED, SEARCHABLE
70
71      contains the RID of a certain group object
72     
73
74   attributeTypes: in schema only
75   objectClasses: in schema only
76   matchingRules: in schema only
77   matchingRuleUse: in schema only
78   creatorsName: not supported by w2k3?
79   modifiersName: not supported by w2k3?
80 */
81
82 #include "ldb_includes.h"
83 #include "ldb_module.h"
84
85 #include "includes.h"
86 #include "dsdb/samdb/samdb.h"
87
88 #ifndef ARRAY_SIZE
89 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
90 #endif
91
92 /*
93   construct a canonical name from a message
94 */
95 static int construct_canonical_name(struct ldb_module *module,
96         struct ldb_message *msg)
97 {
98         char *canonicalName;
99         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
100         if (canonicalName == NULL) {
101                 return -1;
102         }
103         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
104 }
105
106 /*
107   construct a primary group token for groups from a message
108 */
109 static int construct_primary_group_token(struct ldb_module *module,
110         struct ldb_message *msg)
111 {
112         struct ldb_context *ldb;
113         uint32_t primary_group_token;
114
115         ldb = ldb_module_get_ctx(module);
116
117         if (samdb_search_count(ldb, ldb, msg->dn, "(objectclass=group)") == 1) {
118                 primary_group_token
119                         = samdb_result_rid_from_sid(ldb, msg, "objectSid", 0);
120                 return samdb_msg_add_int(ldb, ldb, msg, "primaryGroupToken",
121                         primary_group_token);
122         } else {
123                 return LDB_SUCCESS;
124         }
125 }
126
127
128 /*
129   a list of attribute names that should be substituted in the parse
130   tree before the search is done
131 */
132 static const struct {
133         const char *attr;
134         const char *replace;
135 } parse_tree_sub[] = {
136         { "createTimestamp", "whenCreated" },
137         { "modifyTimestamp", "whenChanged" }
138 };
139
140
141 /*
142   a list of attribute names that are hidden, but can be searched for
143   using another (non-hidden) name to produce the correct result
144 */
145 static const struct {
146         const char *attr;
147         const char *replace;
148         int (*constructor)(struct ldb_module *, struct ldb_message *);
149 } search_sub[] = {
150         { "createTimestamp", "whenCreated", NULL },
151         { "modifyTimestamp", "whenChanged", NULL },
152         { "structuralObjectClass", "objectClass", NULL },
153         { "canonicalName", "distinguishedName", construct_canonical_name },
154         { "primaryGroupToken", "objectSid", construct_primary_group_token }
155 };
156
157 /*
158   post process a search result record. For any search_sub[] attributes that were
159   asked for, we need to call the appropriate copy routine to copy the result
160   into the message, then remove any attributes that we added to the search but
161   were not asked for by the user
162 */
163 static int operational_search_post_process(struct ldb_module *module,
164                                            struct ldb_message *msg,
165                                            const char * const *attrs)
166 {
167         struct ldb_context *ldb;
168         int i, a=0;
169
170         ldb = ldb_module_get_ctx(module);
171
172         for (a=0;attrs && attrs[a];a++) {
173                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
174                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
175                                 continue;
176                         }
177
178                         /* construct the new attribute, using either a supplied
179                            constructor or a simple copy */
180                         if (search_sub[i].constructor) {
181                                 if (search_sub[i].constructor(module, msg) != 0) {
182                                         goto failed;
183                                 }
184                         } else if (ldb_msg_copy_attr(msg,
185                                                      search_sub[i].replace,
186                                                      search_sub[i].attr) != 0) {
187                                 goto failed;
188                         }
189
190                         /* remove the added search attribute, unless it was
191                            asked for by the user */
192                         if (search_sub[i].replace == NULL ||
193                             ldb_attr_in_list(attrs, search_sub[i].replace) ||
194                             ldb_attr_in_list(attrs, "*")) {
195                                 continue;
196                         }
197
198                         ldb_msg_remove_attr(msg, search_sub[i].replace);
199                 }
200         }
201
202         return 0;
203
204 failed:
205         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
206                       "operational_search_post_process failed for attribute '%s'",
207                       attrs[a]);
208         return -1;
209 }
210
211
212 /*
213   hook search operations
214 */
215
216 struct operational_context {
217         struct ldb_module *module;
218         struct ldb_request *req;
219
220         const char * const *attrs;
221 };
222
223 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
224 {
225         struct operational_context *ac;
226         int ret;
227
228         ac = talloc_get_type(req->context, struct operational_context);
229
230         if (!ares) {
231                 return ldb_module_done(ac->req, NULL, NULL,
232                                         LDB_ERR_OPERATIONS_ERROR);
233         }
234         if (ares->error != LDB_SUCCESS) {
235                 return ldb_module_done(ac->req, ares->controls,
236                                         ares->response, ares->error);
237         }
238
239         switch (ares->type) {
240         case LDB_REPLY_ENTRY:
241                 /* for each record returned post-process to add any derived
242                    attributes that have been asked for */
243                 ret = operational_search_post_process(ac->module,
244                                                         ares->message,
245                                                         ac->attrs);
246                 if (ret != 0) {
247                         return ldb_module_done(ac->req, NULL, NULL,
248                                                 LDB_ERR_OPERATIONS_ERROR);
249                 }
250                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
251
252         case LDB_REPLY_REFERRAL:
253                 /* ignore referrals */
254                 break;
255
256         case LDB_REPLY_DONE:
257
258                 return ldb_module_done(ac->req, ares->controls,
259                                         ares->response, LDB_SUCCESS);
260         }
261
262         talloc_free(ares);
263         return LDB_SUCCESS;
264 }
265
266 static int operational_search(struct ldb_module *module, struct ldb_request *req)
267 {
268         struct ldb_context *ldb;
269         struct operational_context *ac;
270         struct ldb_request *down_req;
271         const char **search_attrs = NULL;
272         int i, a;
273         int ret;
274
275         ldb = ldb_module_get_ctx(module);
276
277         ac = talloc(req, struct operational_context);
278         if (ac == NULL) {
279                 return LDB_ERR_OPERATIONS_ERROR;
280         }
281
282         ac->module = module;
283         ac->req = req;
284         ac->attrs = req->op.search.attrs;
285
286         /*  FIXME: We must copy the tree and keep the original
287          *  unmodified. SSS */
288         /* replace any attributes in the parse tree that are
289            searchable, but are stored using a different name in the
290            backend */
291         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
292                 ldb_parse_tree_attr_replace(req->op.search.tree,
293                                             parse_tree_sub[i].attr,
294                                             parse_tree_sub[i].replace);
295         }
296
297         /* in the list of attributes we are looking for, rename any
298            attributes to the alias for any hidden attributes that can
299            be fetched directly using non-hidden names */
300         for (a=0;ac->attrs && ac->attrs[a];a++) {
301                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
302                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
303                             search_sub[i].replace) {
304                                 if (!search_attrs) {
305                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
306                                         if (search_attrs == NULL) {
307                                                 return LDB_ERR_OPERATIONS_ERROR;
308                                         }
309                                 }
310                                 search_attrs[a] = search_sub[i].replace;
311                         }
312                 }
313         }
314
315         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
316                                         req->op.search.base,
317                                         req->op.search.scope,
318                                         req->op.search.tree,
319                                         /* use new set of attrs if any */
320                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
321                                         req->controls,
322                                         ac, operational_callback,
323                                         req);
324         if (ret != LDB_SUCCESS) {
325                 return LDB_ERR_OPERATIONS_ERROR;
326         }
327
328         /* perform the search */
329         return ldb_next_request(module, down_req);
330 }
331
332 static int operational_init(struct ldb_module *ctx)
333 {
334         int ret = 0;
335
336         if (ret != 0) {
337                 return ret;
338         }
339
340         return ldb_next_init(ctx);
341 }
342
343 const struct ldb_module_ops ldb_operational_module_ops = {
344         .name              = "operational",
345         .search            = operational_search,
346         .init_context      = operational_init
347 };