s4:objectclass LDB module - fix header and add my copyright
[samba.git] / source4 / dsdb / samdb / ldb_modules / objectclass.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2006-2008
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2009
6    Copyright (C) Matthias Dieter Wallnöfer 2010
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  *  Name: ldb
24  *
25  *  Component: objectClass sorting and constraint checking module
26  *
27  *  Description: 
28  *  - sort the objectClass attribute into the class
29  *    hierarchy and perform constraint checks (correct RDN name,
30  *    valid parent),
31  *  - fix DNs into 'standard' case
32  *  - Add objectCategory and some other attribute defaults
33  *
34  *  Author: Andrew Bartlett
35  */
36
37
38 #include "includes.h"
39 #include "ldb_module.h"
40 #include "dlinklist.h"
41 #include "dsdb/samdb/samdb.h"
42 #include "librpc/ndr/libndr.h"
43 #include "librpc/gen_ndr/ndr_security.h"
44 #include "libcli/security/security.h"
45 #include "auth/auth.h"
46 #include "param/param.h"
47 #include "../libds/common/flags.h"
48 #include "util.h"
49
50 struct oc_context {
51
52         struct ldb_module *module;
53         struct ldb_request *req;
54
55         struct ldb_reply *search_res;
56
57         int (*step_fn)(struct oc_context *);
58 };
59
60 struct class_list {
61         struct class_list *prev, *next;
62         const struct dsdb_class *objectclass;
63 };
64
65 static struct oc_context *oc_init_context(struct ldb_module *module,
66                                           struct ldb_request *req)
67 {
68         struct ldb_context *ldb;
69         struct oc_context *ac;
70
71         ldb = ldb_module_get_ctx(module);
72
73         ac = talloc_zero(req, struct oc_context);
74         if (ac == NULL) {
75                 ldb_set_errstring(ldb, "Out of Memory");
76                 return NULL;
77         }
78
79         ac->module = module;
80         ac->req = req;
81
82         return ac;
83 }
84
85 static int objectclass_do_add(struct oc_context *ac);
86
87 /* Sort objectClasses into correct order, and validate that all
88  * objectClasses specified actually exist in the schema
89  */
90
91 static int objectclass_sort(struct ldb_module *module,
92                             const struct dsdb_schema *schema,
93                             TALLOC_CTX *mem_ctx,
94                             struct ldb_message_element *objectclass_element,
95                             struct class_list **sorted_out) 
96 {
97         struct ldb_context *ldb;
98         unsigned int i, lowest;
99         struct class_list *unsorted = NULL, *sorted = NULL, *current = NULL, *poss_parent = NULL, *new_parent = NULL, *current_lowest = NULL;
100
101         ldb = ldb_module_get_ctx(module);
102
103         /* DESIGN:
104          *
105          * We work on 4 different 'bins' (implemented here as linked lists):
106          *
107          * * sorted:       the eventual list, in the order we wish to push
108          *                 into the database.  This is the only ordered list.
109          *
110          * * parent_class: The current parent class 'bin' we are
111          *                 trying to find subclasses for
112          *
113          * * subclass:     The subclasses we have found so far
114          *
115          * * unsorted:     The remaining objectClasses
116          *
117          * The process is a matter of filtering objectClasses up from
118          * unsorted into sorted.  Order is irrelevent in the later 3 'bins'.
119          * 
120          * We start with 'top' (found and promoted to parent_class
121          * initially).  Then we find (in unsorted) all the direct
122          * subclasses of 'top'.  parent_classes is concatenated onto
123          * the end of 'sorted', and subclass becomes the list in
124          * parent_class.
125          *
126          * We then repeat, until we find no more subclasses.  Any left
127          * over classes are added to the end.
128          *
129          */
130
131         /* Firstly, dump all the objectClass elements into the
132          * unsorted bin, except for 'top', which is special */
133         for (i=0; i < objectclass_element->num_values; i++) {
134                 current = talloc(mem_ctx, struct class_list);
135                 if (!current) {
136                         ldb_oom(ldb);
137                         return LDB_ERR_OPERATIONS_ERROR;
138                 }
139                 current->objectclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &objectclass_element->values[i]);
140                 if (!current->objectclass) {
141                         ldb_asprintf_errstring(ldb, "objectclass %.*s is not a valid objectClass in schema", 
142                                                (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
143                         /* This looks weird, but windows apparently returns this for invalid objectClass values */
144                         return LDB_ERR_NO_SUCH_ATTRIBUTE;
145                 } else if (current->objectclass->isDefunct) {
146                         ldb_asprintf_errstring(ldb, "objectclass %.*s marked as isDefunct objectClass in schema - not valid for new objects", 
147                                                (int)objectclass_element->values[i].length, (const char *)objectclass_element->values[i].data);
148                         /* This looks weird, but windows apparently returns this for invalid objectClass values */
149                         return LDB_ERR_NO_SUCH_ATTRIBUTE;
150                 }
151
152                 /* Don't add top to list, we will do that later */
153                 if (ldb_attr_cmp("top", current->objectclass->lDAPDisplayName) != 0) {
154                         DLIST_ADD_END(unsorted, current, struct class_list *);
155                 }
156         }
157
158         /* Add top here, to prevent duplicates */
159         current = talloc(mem_ctx, struct class_list);
160         current->objectclass = dsdb_class_by_lDAPDisplayName(schema, "top");
161         DLIST_ADD_END(sorted, current, struct class_list *);
162
163
164         /* For each object:  find parent chain */
165         for (current = unsorted; schema && current; current = current->next) {
166                 for (poss_parent = unsorted; poss_parent; poss_parent = poss_parent->next) {
167                         if (ldb_attr_cmp(poss_parent->objectclass->lDAPDisplayName, current->objectclass->subClassOf) == 0) {
168                                 break;
169                         }
170                 }
171                 /* If we didn't get to the end of the list, we need to add this parent */
172                 if (poss_parent || (ldb_attr_cmp("top", current->objectclass->subClassOf) == 0)) {
173                         continue;
174                 }
175
176                 new_parent = talloc(mem_ctx, struct class_list);
177                 new_parent->objectclass = dsdb_class_by_lDAPDisplayName(schema, current->objectclass->subClassOf);
178                 DLIST_ADD_END(unsorted, new_parent, struct class_list *);
179         }
180
181         do
182         {
183                 lowest = UINT_MAX;
184                 current_lowest = NULL;
185                 for (current = unsorted; schema && current; current = current->next) {
186                         if(current->objectclass->subClass_order < lowest) {
187                                 current_lowest = current;
188                                 lowest = current->objectclass->subClass_order;
189                         }
190                 }
191
192                 if(current_lowest != NULL) {
193                         DLIST_REMOVE(unsorted,current_lowest);
194                         DLIST_ADD_END(sorted,current_lowest, struct class_list *);
195                 }
196         } while(unsorted);
197
198
199         if (!unsorted) {
200                 *sorted_out = sorted;
201                 return LDB_SUCCESS;
202         }
203
204         if (!schema) {
205                 /* If we don't have schema yet, then just merge the lists again */
206                 DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
207                 *sorted_out = sorted;
208                 return LDB_SUCCESS;
209         }
210
211         /* This shouldn't happen, and would break MMC, perhaps there
212          * was no 'top', a conflict in the objectClasses or some other
213          * schema error?
214          */
215         ldb_asprintf_errstring(ldb, "objectclass %s is not a valid objectClass in objectClass chain", unsorted->objectclass->lDAPDisplayName);
216         return LDB_ERR_OBJECT_CLASS_VIOLATION;
217 }
218
219 static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
220 {
221         struct ldb_context *ldb;
222         struct oc_context *ac;
223         int ret;
224
225         ac = talloc_get_type(req->context, struct oc_context);
226         ldb = ldb_module_get_ctx(ac->module);
227
228         if (!ares) {
229                 return ldb_module_done(ac->req, NULL, NULL,
230                                         LDB_ERR_OPERATIONS_ERROR);
231         }
232         if (ares->error != LDB_SUCCESS &&
233             ares->error != LDB_ERR_NO_SUCH_OBJECT) {
234                 return ldb_module_done(ac->req, ares->controls,
235                                         ares->response, ares->error);
236         }
237
238         ldb_reset_err_string(ldb);
239
240         switch (ares->type) {
241         case LDB_REPLY_ENTRY:
242                 if (ac->search_res != NULL) {
243                         ldb_set_errstring(ldb, "Too many results");
244                         talloc_free(ares);
245                         return ldb_module_done(ac->req, NULL, NULL,
246                                                 LDB_ERR_OPERATIONS_ERROR);
247                 }
248
249                 ac->search_res = talloc_steal(ac, ares);
250                 break;
251
252         case LDB_REPLY_REFERRAL:
253                 /* ignore */
254                 talloc_free(ares);
255                 break;
256
257         case LDB_REPLY_DONE:
258                 talloc_free(ares);
259                 ret = ac->step_fn(ac);
260                 if (ret != LDB_SUCCESS) {
261                         return ldb_module_done(ac->req, NULL, NULL, ret);
262                 }
263                 break;
264         }
265
266         return LDB_SUCCESS;
267 }
268
269 static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
270 {
271         struct oc_context *ac;
272
273         ac = talloc_get_type(req->context, struct oc_context);
274
275         if (!ares) {
276                 return ldb_module_done(ac->req, NULL, NULL,
277                                         LDB_ERR_OPERATIONS_ERROR);
278         }
279
280         if (ares->type == LDB_REPLY_REFERRAL) {
281                 return ldb_module_send_referral(ac->req, ares->referral);
282         }
283
284         if (ares->error != LDB_SUCCESS) {
285                 return ldb_module_done(ac->req, ares->controls,
286                                         ares->response, ares->error);
287         }
288
289         if (ares->type != LDB_REPLY_DONE) {
290                 talloc_free(ares);
291                 return ldb_module_done(ac->req, NULL, NULL,
292                                         LDB_ERR_OPERATIONS_ERROR);
293         }
294
295         return ldb_module_done(ac->req, ares->controls,
296                                 ares->response, ares->error);
297 }
298
299 /* Fix up the DN to be in the standard form, taking particular care to match the parent DN
300
301    This should mean that if the parent is:
302     CN=Users,DC=samba,DC=example,DC=com
303    and a proposed child is
304     cn=Admins ,cn=USERS,dc=Samba,dc=example,dc=COM
305
306    The resulting DN should be:
307
308     CN=Admins,CN=Users,DC=samba,DC=example,DC=com
309    
310  */
311 static int fix_dn(TALLOC_CTX *mem_ctx, 
312                   struct ldb_dn *newdn, struct ldb_dn *parent_dn, 
313                   struct ldb_dn **fixed_dn) 
314 {
315         char *upper_rdn_attr;
316         const struct ldb_val *rdn_val;
317
318         /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
319         *fixed_dn = ldb_dn_copy(mem_ctx, parent_dn);
320
321         /* We need the attribute name in upper case */
322         upper_rdn_attr = strupper_talloc(*fixed_dn, 
323                                          ldb_dn_get_rdn_name(newdn));
324         if (!upper_rdn_attr) {
325                 return LDB_ERR_OPERATIONS_ERROR;
326         }
327
328         /* Create a new child */
329         if (ldb_dn_add_child_fmt(*fixed_dn, "X=X") == false) {
330                 return LDB_ERR_OPERATIONS_ERROR;
331         }
332
333
334         rdn_val = ldb_dn_get_rdn_val(newdn);
335
336 #if 0
337         /* the rules for rDN length constraints are more complex than
338         this. Until we understand them we need to leave this
339         constraint out. Otherwise we break replication, as windows
340         does sometimes send us rDNs longer than 64 */
341         if (!rdn_val || rdn_val->length > 64) {
342                 DEBUG(2,(__location__ ": WARNING: rDN longer than 64 limit for '%s'\n", ldb_dn_get_linearized(newdn)));
343         }
344 #endif
345
346
347         /* And replace it with CN=foo (we need the attribute in upper case */
348         return ldb_dn_set_component(*fixed_dn, 0, upper_rdn_attr, *rdn_val);
349 }
350
351 /* Fix all attribute names to be in the correct case, and check they are all valid per the schema */
352 static int fix_check_attributes(struct ldb_context *ldb,
353                                 const struct dsdb_schema *schema,
354                                 struct ldb_message *msg,
355                                 enum ldb_request_type op)
356 {
357         unsigned int i;
358         for (i=0; i < msg->num_elements; i++) {
359                 const struct dsdb_attribute *attribute = dsdb_attribute_by_lDAPDisplayName(schema, msg->elements[i].name);
360                 /* Add in a very special case for 'clearTextPassword',
361                  * which is used for internal processing only, and is
362                  * not presented in the schema */
363                 if (!attribute) {
364                         if (strcasecmp(msg->elements[i].name, "clearTextPassword") != 0) {
365                                 ldb_asprintf_errstring(ldb, "attribute %s is not a valid attribute in schema", msg->elements[i].name);
366                                 /* Apparently Windows sends exactly this behaviour */
367                                 return LDB_ERR_NO_SUCH_ATTRIBUTE;
368                         }
369                 } else {
370                         msg->elements[i].name = attribute->lDAPDisplayName;
371
372                         /* We have to deny write operations on constructed attributes */
373                         if ((attribute->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
374                                 ldb_asprintf_errstring(ldb, "attribute %s is constructed", msg->elements[i].name);
375                                 if (op == LDB_ADD) {
376                                         return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
377                                 } else {
378                                         return LDB_ERR_CONSTRAINT_VIOLATION;
379                                 }
380                         }
381
382                 }
383         }
384
385         return LDB_SUCCESS;
386 }
387
388 static int objectclass_do_add(struct oc_context *ac);
389
390 static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
391 {
392         struct ldb_context *ldb;
393         struct ldb_request *search_req;
394         struct oc_context *ac;
395         struct ldb_dn *parent_dn;
396         int ret;
397         static const char * const parent_attrs[] = { "objectGUID", "objectClass", NULL };
398
399         ldb = ldb_module_get_ctx(module);
400
401         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
402
403         /* do not manipulate our control entries */
404         if (ldb_dn_is_special(req->op.add.message->dn)) {
405                 return ldb_next_request(module, req);
406         }
407
408         /* the objectClass must be specified on add */
409         if (ldb_msg_find_element(req->op.add.message, 
410                                  "objectClass") == NULL) {
411                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
412         }
413
414         ac = oc_init_context(module, req);
415         if (ac == NULL) {
416                 return LDB_ERR_OPERATIONS_ERROR;
417         }
418
419         /* If there isn't a parent, just go on to the add processing */
420         if (ldb_dn_get_comp_num(ac->req->op.add.message->dn) == 1) {
421                 return objectclass_do_add(ac);
422         }
423
424         /* get copy of parent DN */
425         parent_dn = ldb_dn_get_parent(ac, ac->req->op.add.message->dn);
426         if (parent_dn == NULL) {
427                 ldb_oom(ldb);
428                 return LDB_ERR_OPERATIONS_ERROR;
429         }
430
431         ret = ldb_build_search_req(&search_req, ldb,
432                                    ac, parent_dn, LDB_SCOPE_BASE,
433                                    "(objectClass=*)", parent_attrs,
434                                    NULL,
435                                    ac, get_search_callback,
436                                    req);
437         if (ret != LDB_SUCCESS) {
438                 return ret;
439         }
440         talloc_steal(search_req, parent_dn);
441
442         ac->step_fn = objectclass_do_add;
443
444         return ldb_next_request(ac->module, search_req);
445 }
446
447 static int objectclass_do_add(struct oc_context *ac)
448 {
449         struct ldb_context *ldb;
450         const struct dsdb_schema *schema;
451         struct ldb_request *add_req;
452         char *value;
453         struct ldb_message_element *objectclass_element, *el;
454         struct ldb_message *msg;
455         TALLOC_CTX *mem_ctx;
456         struct class_list *sorted, *current;
457         int ret;
458         const struct dsdb_class *objectclass;
459         int32_t systemFlags = 0;
460         const char *rdn_name = NULL;
461
462         ldb = ldb_module_get_ctx(ac->module);
463         schema = dsdb_get_schema(ldb, ac);
464
465         mem_ctx = talloc_new(ac);
466         if (mem_ctx == NULL) {
467                 ldb_oom(ldb);
468                 return LDB_ERR_OPERATIONS_ERROR;
469         }
470
471         msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
472
473         /* Check we have a valid parent */
474         if (ac->search_res == NULL) {
475                 if (ldb_dn_compare(ldb_get_root_basedn(ldb), msg->dn) == 0) {
476                         /* Allow the tree to be started */
477                         
478                         /* but don't keep any error string, it's meaningless */
479                         ldb_set_errstring(ldb, NULL);
480                 } else {
481                         ldb_asprintf_errstring(ldb, "objectclass: Cannot add %s, parent does not exist!", 
482                                                ldb_dn_get_linearized(msg->dn));
483                         talloc_free(mem_ctx);
484                         return LDB_ERR_NO_SUCH_OBJECT;
485                 }
486         } else {
487
488                 /* Fix up the DN to be in the standard form, taking particular care to match the parent DN */
489                 ret = fix_dn(msg, 
490                              ac->req->op.add.message->dn,
491                              ac->search_res->message->dn,
492                              &msg->dn);
493
494                 if (ret != LDB_SUCCESS) {
495                         ldb_asprintf_errstring(ldb, "Could not munge DN %s into normal form", 
496                                                ldb_dn_get_linearized(ac->req->op.add.message->dn));
497                         talloc_free(mem_ctx);
498                         return ret;
499                 }
500
501         }
502         if (schema) {
503                 ret = fix_check_attributes(ldb, schema, msg, ac->req->operation);
504                 if (ret != LDB_SUCCESS) {
505                         talloc_free(mem_ctx);
506                         return ret;
507                 }
508
509                 /* This is now the objectClass list from the database */
510                 objectclass_element = ldb_msg_find_element(msg, "objectClass");
511
512                 if (!objectclass_element) {
513                         /* Where did it go?  bail now... */
514                         talloc_free(mem_ctx);
515                         return LDB_ERR_OPERATIONS_ERROR;
516                 }
517                 ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
518                 if (ret != LDB_SUCCESS) {
519                         talloc_free(mem_ctx);
520                         return ret;
521                 }
522                 
523                 ldb_msg_remove_attr(msg, "objectClass");
524                 ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL);
525                 
526                 if (ret != LDB_SUCCESS) {
527                         talloc_free(mem_ctx);
528                         return ret;
529                 }
530
531                 /* We must completely replace the existing objectClass entry,
532                  * because we need it sorted */
533
534                 /* Move from the linked list back into an ldb msg */
535                 for (current = sorted; current; current = current->next) {
536                         value = talloc_strdup(msg, current->objectclass->lDAPDisplayName);
537                         if (value == NULL) {
538                                 ldb_oom(ldb);
539                                 talloc_free(mem_ctx);
540                                 return LDB_ERR_OPERATIONS_ERROR;
541                         }
542                         ret = ldb_msg_add_string(msg, "objectClass", value);
543                         if (ret != LDB_SUCCESS) {
544                                 ldb_set_errstring(ldb,
545                                                   "objectclass: could not re-add sorted "
546                                                   "objectclass to modify msg");
547                                 talloc_free(mem_ctx);
548                                 return ret;
549                         }
550                 }
551
552                 /* Retrive the message again so get_last_structural_class works */
553                 objectclass_element = ldb_msg_find_element(msg, "objectClass");
554
555                 /* Make sure its valid to add an object of this type */
556                 objectclass = get_last_structural_class(schema,objectclass_element);
557                 if(objectclass == NULL) {
558                         ldb_asprintf_errstring(ldb,
559                                                 "Failed to find a structural class for %s",
560                                                   ldb_dn_get_linearized(msg->dn));
561                         return LDB_ERR_NAMING_VIOLATION;
562                 }
563
564                 rdn_name = ldb_dn_get_rdn_name(msg->dn);
565                 if (objectclass->rDNAttID
566                         && ldb_attr_cmp(rdn_name, objectclass->rDNAttID) != 0) {
567                         ldb_asprintf_errstring(ldb,
568                                                 "RDN %s is not correct for most specific structural objectclass %s, should be %s",
569                                                 rdn_name, objectclass->lDAPDisplayName, objectclass->rDNAttID);
570                         return LDB_ERR_NAMING_VIOLATION;
571                 }
572
573                 if (ac->search_res && ac->search_res->message) {
574                         struct ldb_message_element *oc_el
575                                 = ldb_msg_find_element(ac->search_res->message, "objectClass");
576
577                         bool allowed_class = false;
578                         int i, j;
579                         for (i=0; allowed_class == false && oc_el && i < oc_el->num_values; i++) {
580                                 const struct dsdb_class *sclass;
581
582                                 sclass = dsdb_class_by_lDAPDisplayName_ldb_val(schema, &oc_el->values[i]);
583                                 if (!sclass) {
584                                         /* We don't know this class?  what is going on? */
585                                         continue;
586                                 }
587                                 if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
588                                         for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
589                                                 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
590                                                         allowed_class = true;
591                                                         break;
592                                                 }
593                                         }
594                                 } else {
595                                         for (j=0; sclass->systemPossibleInferiors && sclass->systemPossibleInferiors[j]; j++) {
596                                                 if (ldb_attr_cmp(objectclass->lDAPDisplayName, sclass->systemPossibleInferiors[j]) == 0) {
597                                                         allowed_class = true;
598                                                         break;
599                                                 }
600                                         }
601                                 }
602                         }
603
604                         if (!allowed_class) {
605                                 ldb_asprintf_errstring(ldb, "structural objectClass %s is not a valid child class for %s",
606                                                 objectclass->lDAPDisplayName, ldb_dn_get_linearized(ac->search_res->message->dn));
607                                 return LDB_ERR_NAMING_VIOLATION;
608                         }
609                 }
610
611                 if (objectclass->systemOnly && !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
612                         ldb_asprintf_errstring(ldb, "objectClass %s is systemOnly, rejecting creation of %s",
613                                                 objectclass->lDAPDisplayName, ldb_dn_get_linearized(msg->dn));
614                         return LDB_ERR_UNWILLING_TO_PERFORM;
615                 }
616
617                 if (!ldb_msg_find_element(msg, "objectCategory")) {
618                         struct dsdb_extended_dn_store_format *dn_format = talloc_get_type(ldb_module_get_private(ac->module), struct dsdb_extended_dn_store_format);
619                         if (dn_format && dn_format->store_extended_dn_in_ldb == false) {
620                                 /* Strip off extended components */
621                                 struct ldb_dn *dn = ldb_dn_new(msg, ldb, objectclass->defaultObjectCategory);
622                                 value = ldb_dn_alloc_linearized(msg, dn);
623                                 talloc_free(dn);
624                         } else {
625                                 value = talloc_strdup(msg, objectclass->defaultObjectCategory);
626                         }
627                         if (value == NULL) {
628                                 ldb_oom(ldb);
629                                 talloc_free(mem_ctx);
630                                 return LDB_ERR_OPERATIONS_ERROR;
631                         }
632                         ldb_msg_add_string(msg, "objectCategory", value);
633                 }
634                 if (!ldb_msg_find_element(msg, "showInAdvancedViewOnly") && (objectclass->defaultHidingValue == true)) {
635                         ldb_msg_add_string(msg, "showInAdvancedViewOnly",
636                                                 "TRUE");
637                 }
638
639                 /* There are very special rules for systemFlags, see MS-ADTS 3.1.1.5.2.4 */
640                 el = ldb_msg_find_element(msg, "systemFlags");
641
642                 systemFlags = ldb_msg_find_attr_as_int(msg, "systemFlags", 0);
643
644                 if (el) {
645                         /* Only these flags may be set by a client, but we can't tell between a client and our provision at this point */
646                         /* systemFlags &= ( SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_MOVE | SYSTEM_FLAG_CONFIG_LIMITED_MOVE); */
647                         ldb_msg_remove_element(msg, el);
648                 }
649
650                 /* This flag is only allowed on attributeSchema objects */
651                 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "attributeSchema") == 0) {
652                         systemFlags &= ~SYSTEM_FLAG_ATTR_IS_RDN;
653                 }
654
655                 if (ldb_attr_cmp(objectclass->lDAPDisplayName, "server") == 0) {
656                         systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE | SYSTEM_FLAG_CONFIG_ALLOW_RENAME | SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE);
657                 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "site") == 0
658                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "serverContainer") == 0
659                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "ntDSDSA") == 0) {
660                         systemFlags |= (int32_t)(SYSTEM_FLAG_DISALLOW_MOVE_ON_DELETE);
661
662                 } else if (ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLink") == 0
663                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "siteLinkBridge") == 0
664                                 || ldb_attr_cmp(objectclass->lDAPDisplayName, "nTDSConnection") == 0) {
665                         systemFlags |= (int32_t)(SYSTEM_FLAG_CONFIG_ALLOW_RENAME);
666                 }
667
668                 /* TODO: If parent object is site or subnet, also add (SYSTEM_FLAG_CONFIG_ALLOW_RENAME) */
669
670                 if (el || systemFlags != 0) {
671                         samdb_msg_add_int(ldb, msg, msg, "systemFlags", systemFlags);
672                 }
673         }
674
675         talloc_free(mem_ctx);
676         ret = ldb_msg_sanity_check(ldb, msg);
677
678
679         if (ret != LDB_SUCCESS) {
680                 return ret;
681         }
682
683         ret = ldb_build_add_req(&add_req, ldb, ac,
684                                 msg,
685                                 ac->req->controls,
686                                 ac, oc_op_callback,
687                                 ac->req);
688         if (ret != LDB_SUCCESS) {
689                 return ret;
690         }
691
692         /* perform the add */
693         return ldb_next_request(ac->module, add_req);
694 }
695
696 static int oc_modify_callback(struct ldb_request *req,
697                                 struct ldb_reply *ares);
698 static int objectclass_do_mod(struct oc_context *ac);
699
700 static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
701 {
702         struct ldb_context *ldb = ldb_module_get_ctx(module);
703         struct ldb_message_element *objectclass_element;
704         struct ldb_message *msg;
705         const struct dsdb_schema *schema = dsdb_get_schema(ldb, NULL);
706         struct class_list *sorted, *current;
707         struct ldb_request *down_req;
708         struct oc_context *ac;
709         TALLOC_CTX *mem_ctx;
710         char *value;
711         int ret;
712
713         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
714
715         /* do not manipulate our control entries */
716         if (ldb_dn_is_special(req->op.mod.message->dn)) {
717                 return ldb_next_request(module, req);
718         }
719         
720         /* Without schema, there isn't much to do here */
721         if (!schema) {
722                 return ldb_next_request(module, req);
723         }
724
725         /* As with the "real" AD we don't accept empty messages */
726         if (req->op.mod.message->num_elements == 0) {
727                 ldb_set_errstring(ldb, "objectclass: modify message must have "
728                                        "elements/attributes!");
729                 return LDB_ERR_UNWILLING_TO_PERFORM;
730         }
731
732         ac = oc_init_context(module, req);
733         if (ac == NULL) {
734                 ldb_oom(ldb);
735                 return LDB_ERR_OPERATIONS_ERROR;
736         }
737
738         if (!talloc_reference(ac, schema)) {
739                 ldb_oom(ldb);
740                 return LDB_ERR_OPERATIONS_ERROR;
741         }
742
743         /* If no part of this touches the objectClass, then we don't
744          * need to make any changes.  */
745         objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
746
747         /* If the only operation is the deletion of the objectClass
748          * then go on with just fixing the attribute case */
749         if (!objectclass_element) {
750                 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
751                 if (msg == NULL) {
752                         return LDB_ERR_OPERATIONS_ERROR;
753                 }
754                 
755                 ret = fix_check_attributes(ldb, schema, msg, req->operation);
756                 if (ret != LDB_SUCCESS) {
757                         return ret;
758                 }
759
760                 ret = ldb_build_mod_req(&down_req, ldb, ac,
761                                         msg,
762                                         req->controls,
763                                         ac, oc_op_callback,
764                                         req);
765                 if (ret != LDB_SUCCESS) {
766                         return ret;
767                 }
768
769                 /* go on with the call chain */
770                 return ldb_next_request(module, down_req);
771         }
772
773         switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
774         case LDB_FLAG_MOD_DELETE:
775                 if (objectclass_element->num_values == 0) {
776                         return LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED;
777                 }
778                 break;
779
780         case LDB_FLAG_MOD_REPLACE:
781                 mem_ctx = talloc_new(ac);
782                 if (mem_ctx == NULL) {
783                         return LDB_ERR_OPERATIONS_ERROR;
784                 }
785
786                 msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
787                 if (msg == NULL) {
788                         talloc_free(mem_ctx);
789                         return LDB_ERR_OPERATIONS_ERROR;
790                 }
791
792                 ret = fix_check_attributes(ldb, schema, msg, req->operation);
793                 if (ret != LDB_SUCCESS) {
794                         talloc_free(mem_ctx);
795                         return ret;
796                 }
797
798                 ret = objectclass_sort(module, schema, mem_ctx, objectclass_element, &sorted);
799                 if (ret != LDB_SUCCESS) {
800                         talloc_free(mem_ctx);
801                         return ret;
802                 }
803
804                 /* We must completely replace the existing objectClass entry,
805                  * because we need it sorted */
806                 
807                 ldb_msg_remove_attr(msg, "objectClass");
808                 ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
809                 
810                 if (ret != LDB_SUCCESS) {
811                         talloc_free(mem_ctx);
812                         return ret;
813                 }
814
815                 /* Move from the linked list back into an ldb msg */
816                 for (current = sorted; current; current = current->next) {
817                         /* copy the value as this string is on the schema
818                          * context and we can't rely on it not changing
819                          * before the operation is over */
820                         value = talloc_strdup(msg,
821                                         current->objectclass->lDAPDisplayName);
822                         if (value == NULL) {
823                                 ldb_oom(ldb);
824                                 talloc_free(mem_ctx);
825                                 return LDB_ERR_OPERATIONS_ERROR;
826                         }
827                         ret = ldb_msg_add_string(msg, "objectClass", value);
828                         if (ret != LDB_SUCCESS) {
829                                 ldb_set_errstring(ldb,
830                                         "objectclass: could not re-add sorted "
831                                         "objectclass to modify msg");
832                                 talloc_free(mem_ctx);
833                                 return ret;
834                         }
835                 }
836                 
837                 talloc_free(mem_ctx);
838
839                 ret = ldb_msg_sanity_check(ldb, msg);
840                 if (ret != LDB_SUCCESS) {
841                         return ret;
842                 }
843
844                 ret = ldb_build_mod_req(&down_req, ldb, ac,
845                                         msg,
846                                         req->controls,
847                                         ac, oc_op_callback,
848                                         req);
849                 if (ret != LDB_SUCCESS) {
850                         return ret;
851                 }
852
853                 /* go on with the call chain */
854                 return ldb_next_request(module, down_req);
855         }
856
857         /* This isn't the default branch of the switch, but a 'in any
858          * other case'.  When a delete isn't for all objectClasses for
859          * example
860          */
861
862         msg = ldb_msg_copy_shallow(ac, req->op.mod.message);
863         if (msg == NULL) {
864                 ldb_oom(ldb);
865                 return LDB_ERR_OPERATIONS_ERROR;
866         }
867
868         ret = fix_check_attributes(ldb, schema, msg, req->operation);
869         if (ret != LDB_SUCCESS) {
870                 ldb_oom(ldb);
871                 return ret;
872         }
873
874         ret = ldb_build_mod_req(&down_req, ldb, ac,
875                                 msg,
876                                 req->controls,
877                                 ac, oc_modify_callback,
878                                 req);
879         if (ret != LDB_SUCCESS) {
880                 return ret;
881         }
882
883         return ldb_next_request(module, down_req);
884 }
885
886 static int oc_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
887 {
888         struct ldb_context *ldb;
889         static const char * const attrs[] = { "objectClass", NULL };
890         struct ldb_request *search_req;
891         struct oc_context *ac;
892         int ret;
893
894         ac = talloc_get_type(req->context, struct oc_context);
895         ldb = ldb_module_get_ctx(ac->module);
896
897         if (!ares) {
898                 return ldb_module_done(ac->req, NULL, NULL,
899                                         LDB_ERR_OPERATIONS_ERROR);
900         }
901
902         if (ares->type == LDB_REPLY_REFERRAL) {
903                 return ldb_module_send_referral(ac->req, ares->referral);
904         }
905
906         if (ares->error != LDB_SUCCESS) {
907                 return ldb_module_done(ac->req, ares->controls,
908                                         ares->response, ares->error);
909         }
910
911         if (ares->type != LDB_REPLY_DONE) {
912                 talloc_free(ares);
913                 return ldb_module_done(ac->req, NULL, NULL,
914                                         LDB_ERR_OPERATIONS_ERROR);
915         }
916
917         talloc_free(ares);
918
919         ret = ldb_build_search_req(&search_req, ldb, ac,
920                                    ac->req->op.mod.message->dn, LDB_SCOPE_BASE,
921                                    "(objectClass=*)",
922                                    attrs, NULL, 
923                                    ac, get_search_callback,
924                                    ac->req);
925         if (ret != LDB_SUCCESS) {
926                 return ldb_module_done(ac->req, NULL, NULL, ret);
927         }
928
929         ac->step_fn = objectclass_do_mod;
930
931         ret = ldb_next_request(ac->module, search_req);
932         if (ret != LDB_SUCCESS) {
933                 return ldb_module_done(ac->req, NULL, NULL, ret);
934         }
935         return LDB_SUCCESS;
936 }
937
938 static int objectclass_do_mod(struct oc_context *ac)
939 {
940         struct ldb_context *ldb;
941         const struct dsdb_schema *schema;
942         struct ldb_request *mod_req;
943         char *value;
944         struct ldb_message_element *objectclass_element;
945         struct ldb_message *msg;
946         TALLOC_CTX *mem_ctx;
947         struct class_list *sorted, *current;
948         int ret;
949
950         ldb = ldb_module_get_ctx(ac->module);
951
952         if (ac->search_res == NULL) {
953                 return LDB_ERR_OPERATIONS_ERROR;
954         }
955         schema = dsdb_get_schema(ldb, ac);
956
957         mem_ctx = talloc_new(ac);
958         if (mem_ctx == NULL) {
959                 return LDB_ERR_OPERATIONS_ERROR;
960         }
961
962         /* use a new message structure */
963         msg = ldb_msg_new(ac);
964         if (msg == NULL) {
965                 ldb_set_errstring(ldb,
966                         "objectclass: could not create new modify msg");
967                 talloc_free(mem_ctx);
968                 return LDB_ERR_OPERATIONS_ERROR;
969         }
970
971         /* This is now the objectClass list from the database */
972         objectclass_element = ldb_msg_find_element(ac->search_res->message, 
973                                                    "objectClass");
974         if (!objectclass_element) {
975                 /* Where did it go?  bail now... */
976                 talloc_free(mem_ctx);
977                 return LDB_ERR_OPERATIONS_ERROR;
978         }
979         
980         /* modify dn */
981         msg->dn = ac->req->op.mod.message->dn;
982
983         ret = objectclass_sort(ac->module, schema, mem_ctx, objectclass_element, &sorted);
984         if (ret != LDB_SUCCESS) {
985                 return ret;
986         }
987
988         /* We must completely replace the existing objectClass entry.
989          * We could do a constrained add/del, but we are meant to be
990          * in a transaction... */
991
992         ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
993         if (ret != LDB_SUCCESS) {
994                 ldb_set_errstring(ldb, "objectclass: could not clear objectclass in modify msg");
995                 talloc_free(mem_ctx);
996                 return ret;
997         }
998         
999         /* Move from the linked list back into an ldb msg */
1000         for (current = sorted; current; current = current->next) {
1001                 value = talloc_strdup(msg, current->objectclass->lDAPDisplayName);
1002                 if (value == NULL) {
1003                         ldb_oom(ldb);
1004                         return LDB_ERR_OPERATIONS_ERROR;
1005                 }
1006                 ret = ldb_msg_add_string(msg, "objectClass", value);
1007                 if (ret != LDB_SUCCESS) {
1008                         ldb_set_errstring(ldb, "objectclass: could not re-add sorted objectclass to modify msg");
1009                         talloc_free(mem_ctx);
1010                         return ret;
1011                 }
1012         }
1013
1014         ret = ldb_msg_sanity_check(ldb, msg);
1015         if (ret != LDB_SUCCESS) {
1016                 talloc_free(mem_ctx);
1017                 return ret;
1018         }
1019
1020         ret = ldb_build_mod_req(&mod_req, ldb, ac,
1021                                 msg,
1022                                 ac->req->controls,
1023                                 ac, oc_op_callback,
1024                                 ac->req);
1025         if (ret != LDB_SUCCESS) {
1026                 talloc_free(mem_ctx);
1027                 return ret;
1028         }
1029
1030         talloc_free(mem_ctx);
1031         /* perform the modify */
1032         return ldb_next_request(ac->module, mod_req);
1033 }
1034
1035 static int objectclass_do_rename(struct oc_context *ac);
1036
1037 static int objectclass_rename(struct ldb_module *module, struct ldb_request *req)
1038 {
1039         static const char * const attrs[] = { NULL };
1040         struct ldb_context *ldb;
1041         struct ldb_request *search_req;
1042         struct oc_context *ac;
1043         struct ldb_dn *parent_dn;
1044         int ret;
1045
1046         ldb = ldb_module_get_ctx(module);
1047
1048         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_rename\n");
1049
1050         if (ldb_dn_is_special(req->op.rename.newdn)) { /* do not manipulate our control entries */
1051                 return ldb_next_request(module, req);
1052         }
1053
1054         /* Firstly ensure we are not trying to rename it to be a child of itself */
1055         if ((ldb_dn_compare_base(req->op.rename.olddn, req->op.rename.newdn) == 0) 
1056             && (ldb_dn_compare(req->op.rename.olddn, req->op.rename.newdn) != 0)) {
1057                 ldb_asprintf_errstring(ldb, "Cannot rename %s to be a child of itself",
1058                                        ldb_dn_get_linearized(req->op.rename.olddn));
1059                 return LDB_ERR_UNWILLING_TO_PERFORM;
1060         }
1061
1062         ac = oc_init_context(module, req);
1063         if (ac == NULL) {
1064                 return LDB_ERR_OPERATIONS_ERROR;
1065         }
1066
1067         parent_dn = ldb_dn_get_parent(ac, req->op.rename.newdn);
1068         if (parent_dn == NULL) {
1069                 ldb_oom(ldb);
1070                 return LDB_ERR_OPERATIONS_ERROR;
1071         }
1072
1073         /*
1074           it makes a search request, looking for the parent DN to fix up the new DN
1075           to a standard one, at objectclass_do_rename()
1076          */
1077         ret = ldb_build_search_req(&search_req, ldb,
1078                                    ac, parent_dn, LDB_SCOPE_BASE,
1079                                    "(objectClass=*)",
1080                                    attrs, NULL,
1081                                    ac, get_search_callback,
1082                                    req);
1083         if (ret != LDB_SUCCESS) {
1084                 return ret;
1085         }
1086
1087         /* we have to add the show deleted control, as otherwise DRS
1088            deletes will be refused as we will think the target parent
1089            does not exist */
1090         ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL);
1091
1092         if (ret != LDB_SUCCESS) {
1093                 return ret;
1094         }
1095
1096         ac->step_fn = objectclass_do_rename;
1097
1098         return ldb_next_request(ac->module, search_req);
1099
1100
1101 }
1102
1103 static int objectclass_do_rename(struct oc_context *ac)
1104 {
1105         struct ldb_context *ldb;
1106         struct ldb_request *rename_req;
1107         struct ldb_dn *fixed_dn;
1108         int ret;
1109
1110         ldb = ldb_module_get_ctx(ac->module);
1111
1112         /* Check we have a valid parent */
1113         if (ac->search_res == NULL) {
1114                 ldb_asprintf_errstring(ldb, "objectclass: Cannot rename %s, parent does not exist!", 
1115                                        ldb_dn_get_linearized(ac->req->op.rename.newdn));
1116                 return LDB_ERR_UNWILLING_TO_PERFORM;
1117         }
1118         
1119         /* Fix up the DN to be in the standard form,
1120          * taking particular care to match the parent DN */
1121         ret = fix_dn(ac,
1122                      ac->req->op.rename.newdn,
1123                      ac->search_res->message->dn,
1124                      &fixed_dn);
1125         if (ret != LDB_SUCCESS) {
1126                 return ret;
1127         }
1128
1129         /* TODO: Check this is a valid child to this parent,
1130          * by reading the allowedChildClasses and
1131          * allowedChildClasssesEffective attributes */
1132
1133         ret = ldb_build_rename_req(&rename_req, ldb, ac,
1134                                    ac->req->op.rename.olddn, fixed_dn,
1135                                    ac->req->controls,
1136                                    ac, oc_op_callback,
1137                                    ac->req);
1138         if (ret != LDB_SUCCESS) {
1139                 return ret;
1140         }
1141
1142         /* perform the rename */
1143         return ldb_next_request(ac->module, rename_req);
1144 }
1145
1146 static int objectclass_init(struct ldb_module *module)
1147 {
1148         struct ldb_context *ldb = ldb_module_get_ctx(module);
1149         int ret;
1150         /* Init everything else */
1151         ret = ldb_next_init(module);
1152         if (ret != LDB_SUCCESS) {
1153                 return ret;
1154         }
1155         
1156         /* Look for the opaque to indicate we might have to cut down the DN of defaultObjectCategory */
1157         ldb_module_set_private(module, ldb_get_opaque(ldb, DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME));
1158
1159         return ret;
1160 }
1161
1162 _PUBLIC_ const struct ldb_module_ops ldb_objectclass_module_ops = {
1163         .name              = "objectclass",
1164         .add           = objectclass_add,
1165         .modify        = objectclass_modify,
1166         .rename        = objectclass_rename,
1167         .init_context  = objectclass_init
1168 };