s4:operational LDB module - make the counters unsigned
[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 #include "dsdb/samdb/ldb_modules/util.h"
74
75 #include "auth/auth.h"
76 #include "libcli/security/dom_sid.h"
77
78 #ifndef ARRAY_SIZE
79 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
80 #endif
81
82 struct operational_data {
83         struct ldb_dn *aggregate_dn;
84 };
85
86 /*
87   construct a canonical name from a message
88 */
89 static int construct_canonical_name(struct ldb_module *module,
90         struct ldb_message *msg)
91 {
92         char *canonicalName;
93         canonicalName = ldb_dn_canonical_string(msg, msg->dn);
94         if (canonicalName == NULL) {
95                 return LDB_ERR_OPERATIONS_ERROR;
96         }
97         return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
98 }
99
100 /*
101   construct a primary group token for groups from a message
102 */
103 static int construct_primary_group_token(struct ldb_module *module,
104                                          struct ldb_message *msg)
105 {
106         struct ldb_context *ldb;
107         uint32_t primary_group_token;
108         
109         ldb = ldb_module_get_ctx(module);
110         if (ldb_match_msg_objectclass(msg, "group") == 1) {
111                 primary_group_token
112                         = samdb_result_rid_from_sid(msg, msg, "objectSid", 0);
113                 if (primary_group_token == 0) {
114                         return LDB_SUCCESS;
115                 }
116
117                 return samdb_msg_add_int(ldb, msg, msg, "primaryGroupToken",
118                         primary_group_token);
119         } else {
120                 return LDB_SUCCESS;
121         }
122 }
123
124 /*
125   construct the token groups for SAM objects from a message
126 */
127 static int construct_token_groups(struct ldb_module *module,
128                                   struct ldb_message *msg)
129 {
130         struct ldb_context *ldb;
131         const struct dom_sid *sid;
132
133         ldb = ldb_module_get_ctx(module);
134
135         sid = samdb_result_dom_sid(msg, msg, "objectSid");
136         if (sid != NULL) {
137                 NTSTATUS status;
138                 uint32_t prim_group_rid;
139                 struct dom_sid **sids = NULL;
140                 unsigned int i, num_sids = 0;
141                 int ret;
142
143                 prim_group_rid = samdb_result_uint(msg, "primaryGroupID", 0);
144                 if (prim_group_rid != 0) {
145                         struct dom_sid *prim_group_sid;
146
147                         prim_group_sid = dom_sid_add_rid(msg,
148                                                          samdb_domain_sid(ldb),
149                                                          prim_group_rid);
150                         if (prim_group_sid == NULL) {
151                                 ldb_oom(ldb);
152                                 return LDB_ERR_OPERATIONS_ERROR;
153                         }
154
155                         /* onlyChilds = false, we want to consider also the
156                          * "primaryGroupID" for membership */
157                         status = authsam_expand_nested_groups(ldb,
158                                                               prim_group_sid,
159                                                               false, msg,
160                                                               &sids, &num_sids);
161                         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
162                                 ldb_oom(ldb);
163                                 return LDB_ERR_OPERATIONS_ERROR;
164                         }
165                         if (!NT_STATUS_IS_OK(status)) {
166                                 return LDB_ERR_OPERATIONS_ERROR;
167                         }
168
169                         for (i = 0; i < num_sids; i++) {
170                                 ret = samdb_msg_add_dom_sid(ldb, msg, msg,
171                                                             "tokenGroups",
172                                                             sids[i]);
173                                 if (ret != LDB_SUCCESS) {
174                                         talloc_free(sids);
175                                         return ret;
176                                 }
177                         }
178
179                         talloc_free(sids);
180                 }
181
182                 sids = NULL;
183                 num_sids = 0;
184
185                 /* onlyChils = true, we don't want to have the SAM object itself
186                  * in the result */
187                 status = authsam_expand_nested_groups(ldb, sid, true, msg,
188                                                       &sids, &num_sids);
189                 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MEMORY)) {
190                         ldb_oom(ldb);
191                         return LDB_ERR_OPERATIONS_ERROR;
192                 }
193                 if (!NT_STATUS_IS_OK(status)) {
194                         return LDB_ERR_OPERATIONS_ERROR;
195                 }
196
197                 for (i = 0; i < num_sids; i++) {
198                         ret = samdb_msg_add_dom_sid(ldb, msg, msg,
199                                                     "tokenGroups", sids[i]);
200                         if (ret != LDB_SUCCESS) {
201                                 talloc_free(sids);
202                                 return ret;
203                         }
204                 }
205
206                 talloc_free(sids);
207         }
208
209         return LDB_SUCCESS;
210 }
211
212 /*
213   construct the parent GUID for an entry from a message
214 */
215 static int construct_parent_guid(struct ldb_module *module,
216                                  struct ldb_message *msg)
217 {
218         struct ldb_result *res;
219         const struct ldb_val *parent_guid;
220         const char *attrs[] = { "objectGUID", NULL };
221         int ret;
222         struct ldb_val v;
223
224         /* TODO:  In the future, this needs to honour the partition boundaries */
225         struct ldb_dn *parent_dn = ldb_dn_get_parent(msg, msg->dn);
226
227         if (parent_dn == NULL) {
228                 DEBUG(4,(__location__ ": Failed to find parent for dn %s\n",
229                                          ldb_dn_get_linearized(msg->dn)));
230                 return LDB_SUCCESS;
231         }
232
233         ret = dsdb_module_search_dn(module, msg, &res, parent_dn, attrs, DSDB_SEARCH_SHOW_DELETED);
234         talloc_free(parent_dn);
235         /* if there is no parentGUID for this object, then return */
236         if (ret == LDB_ERR_NO_SUCH_OBJECT) {
237                 DEBUG(4,(__location__ ": Parent dn for %s does not exist \n",
238                          ldb_dn_get_linearized(msg->dn)));
239                 return LDB_SUCCESS;
240         } else if (ret != LDB_SUCCESS) {
241                 return ret;
242         }
243
244         parent_guid = ldb_msg_find_ldb_val(res->msgs[0], "objectGUID");
245         if (!parent_guid) {
246                 talloc_free(res);
247                 return LDB_SUCCESS;
248         }
249
250         v = data_blob_dup_talloc(res, parent_guid);
251         if (!v.data) {
252                 talloc_free(res);
253                 return LDB_ERR_OPERATIONS_ERROR;
254         }
255         ret = ldb_msg_add_steal_value(msg, "parentGUID", &v);
256         talloc_free(res);
257         return ret;
258 }
259
260 /*
261   construct a subSchemaSubEntry
262 */
263 static int construct_subschema_subentry(struct ldb_module *module,
264                                         struct ldb_message *msg)
265 {
266         struct operational_data *data = talloc_get_type(ldb_module_get_private(module), struct operational_data);
267         char *subSchemaSubEntry;
268         if (data && data->aggregate_dn) {
269                 subSchemaSubEntry = ldb_dn_alloc_linearized(msg, data->aggregate_dn);
270                 return ldb_msg_add_steal_string(msg, "subSchemaSubEntry", subSchemaSubEntry);
271         }
272         return LDB_SUCCESS;
273 }
274
275
276 /*
277   a list of attribute names that should be substituted in the parse
278   tree before the search is done
279 */
280 static const struct {
281         const char *attr;
282         const char *replace;
283 } parse_tree_sub[] = {
284         { "createTimestamp", "whenCreated" },
285         { "modifyTimestamp", "whenChanged" }
286 };
287
288
289 /*
290   a list of attribute names that are hidden, but can be searched for
291   using another (non-hidden) name to produce the correct result
292 */
293 static const struct {
294         const char *attr;
295         const char *replace;
296         const char *extra_attr;
297         int (*constructor)(struct ldb_module *, struct ldb_message *);
298 } search_sub[] = {
299         { "createTimestamp", "whenCreated", NULL , NULL },
300         { "modifyTimestamp", "whenChanged", NULL , NULL },
301         { "structuralObjectClass", "objectClass", NULL , NULL },
302         { "canonicalName", "distinguishedName", NULL , construct_canonical_name },
303         { "primaryGroupToken", "objectClass", "objectSid", construct_primary_group_token },
304         { "tokenGroups", "objectSid", "primaryGroupID", construct_token_groups },
305         { "parentGUID", NULL, NULL, construct_parent_guid },
306         { "subSchemaSubEntry", NULL, NULL, construct_subschema_subentry }
307 };
308
309
310 enum op_remove {
311         OPERATIONAL_REMOVE_ALWAYS, /* remove always */
312         OPERATIONAL_REMOVE_UNASKED,/* remove if not requested */
313         OPERATIONAL_SD_FLAGS       /* show if SD_FLAGS_OID set, or asked for */
314 };
315
316 /*
317   a list of attributes that may need to be removed from the
318   underlying db return
319 */
320 static const struct {
321         const char *attr;
322         enum op_remove op;
323 } operational_remove[] = {
324         { "nTSecurityDescriptor",    OPERATIONAL_SD_FLAGS },
325         { "parentGUID",              OPERATIONAL_REMOVE_ALWAYS  },
326         { "replPropertyMetaData",    OPERATIONAL_REMOVE_UNASKED },
327         { "unicodePwd",              OPERATIONAL_REMOVE_UNASKED },
328         { "dBCSPwd",                 OPERATIONAL_REMOVE_UNASKED },
329         { "ntPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
330         { "lmPwdHistory",            OPERATIONAL_REMOVE_UNASKED },
331         { "supplementalCredentials", OPERATIONAL_REMOVE_UNASKED }
332 };
333
334
335 /*
336   post process a search result record. For any search_sub[] attributes that were
337   asked for, we need to call the appropriate copy routine to copy the result
338   into the message, then remove any attributes that we added to the search but
339   were not asked for by the user
340 */
341 static int operational_search_post_process(struct ldb_module *module,
342                                            struct ldb_message *msg,
343                                            const char * const *attrs,
344                                            bool sd_flags_set)
345 {
346         struct ldb_context *ldb;
347         unsigned int i, a = 0;
348
349         ldb = ldb_module_get_ctx(module);
350
351         /* removed any attrs that should not be shown to the user */
352         for (i=0; i<ARRAY_SIZE(operational_remove); i++) {
353                 switch (operational_remove[i].op) {
354                 case OPERATIONAL_REMOVE_UNASKED:
355                         if (ldb_attr_in_list(attrs, operational_remove[i].attr)) {
356                                 continue;
357                         }
358                 case OPERATIONAL_REMOVE_ALWAYS:
359                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
360                         break;
361                 case OPERATIONAL_SD_FLAGS:
362                         if (sd_flags_set ||
363                             ldb_attr_in_list(attrs, operational_remove[i].attr)) {
364                                 continue;
365                         }
366                         ldb_msg_remove_attr(msg, operational_remove[i].attr);
367                         break;
368                 }
369         }
370
371         for (a=0;attrs && attrs[a];a++) {
372                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
373                         if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
374                                 continue;
375                         }
376
377                         /* construct the new attribute, using either a supplied
378                            constructor or a simple copy */
379                         if (search_sub[i].constructor != NULL) {
380                                 if (search_sub[i].constructor(module, msg) != LDB_SUCCESS) {
381                                         goto failed;
382                                 }
383                         } else if (ldb_msg_copy_attr(msg,
384                                                      search_sub[i].replace,
385                                                      search_sub[i].attr) != LDB_SUCCESS) {
386                                 goto failed;
387                         }
388
389                         /* remove the added search attribute, unless it was
390                            asked for by the user */
391                         if (search_sub[i].replace != NULL && 
392                             !ldb_attr_in_list(attrs, search_sub[i].replace) &&
393                             !ldb_attr_in_list(attrs, "*")) {
394                                 ldb_msg_remove_attr(msg, search_sub[i].replace);
395                         }
396                         if (search_sub[i].extra_attr != NULL && 
397                             !ldb_attr_in_list(attrs, search_sub[i].extra_attr) &&
398                             !ldb_attr_in_list(attrs, "*")) {
399                                 ldb_msg_remove_attr(msg, search_sub[i].extra_attr);
400                         }
401                 }
402         }
403
404         return 0;
405
406 failed:
407         ldb_debug_set(ldb, LDB_DEBUG_WARNING,
408                       "operational_search_post_process failed for attribute '%s'",
409                       attrs[a]);
410         return -1;
411 }
412
413
414 /*
415   hook search operations
416 */
417
418 struct operational_context {
419         struct ldb_module *module;
420         struct ldb_request *req;
421
422         const char * const *attrs;
423         bool sd_flags_set;
424 };
425
426 static int operational_callback(struct ldb_request *req, struct ldb_reply *ares)
427 {
428         struct operational_context *ac;
429         int ret;
430
431         ac = talloc_get_type(req->context, struct operational_context);
432
433         if (!ares) {
434                 return ldb_module_done(ac->req, NULL, NULL,
435                                         LDB_ERR_OPERATIONS_ERROR);
436         }
437         if (ares->error != LDB_SUCCESS) {
438                 return ldb_module_done(ac->req, ares->controls,
439                                         ares->response, ares->error);
440         }
441
442         switch (ares->type) {
443         case LDB_REPLY_ENTRY:
444                 /* for each record returned post-process to add any derived
445                    attributes that have been asked for */
446                 ret = operational_search_post_process(ac->module,
447                                                       ares->message,
448                                                       ac->attrs,
449                                                       ac->sd_flags_set);
450                 if (ret != 0) {
451                         return ldb_module_done(ac->req, NULL, NULL,
452                                                 LDB_ERR_OPERATIONS_ERROR);
453                 }
454                 return ldb_module_send_entry(ac->req, ares->message, ares->controls);
455
456         case LDB_REPLY_REFERRAL:
457                 return ldb_module_send_referral(ac->req, ares->referral);
458
459         case LDB_REPLY_DONE:
460
461                 return ldb_module_done(ac->req, ares->controls,
462                                         ares->response, LDB_SUCCESS);
463         }
464
465         talloc_free(ares);
466         return LDB_SUCCESS;
467 }
468
469 static int operational_search(struct ldb_module *module, struct ldb_request *req)
470 {
471         struct ldb_context *ldb;
472         struct operational_context *ac;
473         struct ldb_request *down_req;
474         const char **search_attrs = NULL;
475         unsigned int i, a;
476         int ret;
477
478         ldb = ldb_module_get_ctx(module);
479
480         ac = talloc(req, struct operational_context);
481         if (ac == NULL) {
482                 return LDB_ERR_OPERATIONS_ERROR;
483         }
484
485         ac->module = module;
486         ac->req = req;
487         ac->attrs = req->op.search.attrs;
488
489         /*  FIXME: We must copy the tree and keep the original
490          *  unmodified. SSS */
491         /* replace any attributes in the parse tree that are
492            searchable, but are stored using a different name in the
493            backend */
494         for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
495                 ldb_parse_tree_attr_replace(req->op.search.tree,
496                                             parse_tree_sub[i].attr,
497                                             parse_tree_sub[i].replace);
498         }
499
500         /* in the list of attributes we are looking for, rename any
501            attributes to the alias for any hidden attributes that can
502            be fetched directly using non-hidden names */
503         for (a=0;ac->attrs && ac->attrs[a];a++) {
504                 for (i=0;i<ARRAY_SIZE(search_sub);i++) {
505                         if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
506                             search_sub[i].replace) {
507
508                                 if (search_sub[i].extra_attr) {
509                                         const char **search_attrs2;
510                                         /* Only adds to the end of the list */
511                                         search_attrs2 = ldb_attr_list_copy_add(req, search_attrs
512                                                                                ? search_attrs
513                                                                                : ac->attrs, 
514                                                                                search_sub[i].extra_attr);
515                                         if (search_attrs2 == NULL) {
516                                                 return LDB_ERR_OPERATIONS_ERROR;
517                                         }
518                                         /* may be NULL, talloc_free() doesn't mind */
519                                         talloc_free(search_attrs);
520                                         search_attrs = search_attrs2;
521                                 }
522
523                                 if (!search_attrs) {
524                                         search_attrs = ldb_attr_list_copy(req, ac->attrs);
525                                         if (search_attrs == NULL) {
526                                                 return LDB_ERR_OPERATIONS_ERROR;
527                                         }
528                                 }
529                                 /* Despite the ldb_attr_list_copy_add, this is safe as that fn only adds to the end */
530                                 search_attrs[a] = search_sub[i].replace;
531                         }
532                 }
533         }
534
535         /* remember if the SD_FLAGS_OID was set */
536         ac->sd_flags_set = (ldb_request_get_control(req, LDB_CONTROL_SD_FLAGS_OID) != NULL);
537
538         ret = ldb_build_search_req_ex(&down_req, ldb, ac,
539                                         req->op.search.base,
540                                         req->op.search.scope,
541                                         req->op.search.tree,
542                                         /* use new set of attrs if any */
543                                         search_attrs == NULL?req->op.search.attrs:search_attrs,
544                                         req->controls,
545                                         ac, operational_callback,
546                                         req);
547         if (ret != LDB_SUCCESS) {
548                 return LDB_ERR_OPERATIONS_ERROR;
549         }
550
551         /* perform the search */
552         return ldb_next_request(module, down_req);
553 }
554
555 static int operational_init(struct ldb_module *ctx)
556 {
557         struct operational_data *data;
558         struct ldb_context *ldb = ldb_module_get_ctx(ctx);
559         int ret = ldb_next_init(ctx);
560
561         if (ret != LDB_SUCCESS) {
562                 return ret;
563         }
564
565         data = talloc(ctx, struct operational_data);
566         if (!data) {
567                 ldb_module_oom(ctx);
568                 return LDB_ERR_OPERATIONS_ERROR;
569         }
570
571         data->aggregate_dn = samdb_aggregate_schema_dn(ldb, data);
572         if (!data->aggregate_dn) {
573                 ldb_set_errstring(ldb, "Could not build aggregate schema DN");
574                 return LDB_ERR_OPERATIONS_ERROR;
575         }
576
577         ldb_module_set_private(ctx, data);
578
579         return LDB_SUCCESS;
580 }
581
582 const struct ldb_module_ops ldb_operational_module_ops = {
583         .name              = "operational",
584         .search            = operational_search,
585         .init_context      = operational_init
586 };