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