s3: piddir creation fix part 2.
[ira/wip.git] / source4 / dsdb / samdb / ldb_modules / objectclass_attrs.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) Stefan Metzmacher 2009
7    Copyright (C) Matthias Dieter Wallnöfer 2010
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18
19    You should have received a copy of the GNU Lesser General Public
20    License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /*
24  *  Name: ldb
25  *
26  *  Component: objectclass attribute checking module
27  *
28  *  Description: this checks the attributes on a directory entry (if they're
29  *    allowed, if the syntax is correct, if mandatory ones are missing,
30  *    denies the deletion of mandatory ones...). The module contains portions
31  *    of the "objectclass" and the "validate_update" LDB module.
32  *
33  *  Author: Matthias Dieter Wallnöfer
34  */
35
36 #include "includes.h"
37 #include "ldb_module.h"
38 #include "dsdb/samdb/samdb.h"
39
40 struct oc_context {
41
42         struct ldb_module *module;
43         struct ldb_request *req;
44         const struct dsdb_schema *schema;
45
46         struct ldb_message *msg;
47
48         struct ldb_reply *search_res;
49         struct ldb_reply *mod_ares;
50 };
51
52 static struct oc_context *oc_init_context(struct ldb_module *module,
53                                           struct ldb_request *req)
54 {
55         struct ldb_context *ldb;
56         struct oc_context *ac;
57
58         ldb = ldb_module_get_ctx(module);
59
60         ac = talloc_zero(req, struct oc_context);
61         if (ac == NULL) {
62                 ldb_oom(ldb);
63                 return NULL;
64         }
65
66         ac->module = module;
67         ac->req = req;
68         ac->schema = dsdb_get_schema(ldb, ac);
69
70         return ac;
71 }
72
73 static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares);
74
75 /*
76  * Checks the correctness of the "dSHeuristics" attribute as described in both
77  * MS-ADTS 7.1.1.2.4.1.2 dSHeuristics and MS-ADTS 3.1.1.5.3.2 Constraints
78  */
79 static int oc_validate_dsheuristics(struct ldb_message_element *el)
80 {
81         if (el->num_values > 0) {
82                 if ((el->values[0].length >= DS_HR_NINETIETH_CHAR) &&
83                     (el->values[0].data[DS_HR_NINETIETH_CHAR-1] != '9')) {
84                         return LDB_ERR_CONSTRAINT_VIOLATION;
85                 }
86                 if ((el->values[0].length >= DS_HR_EIGHTIETH_CHAR) &&
87                     (el->values[0].data[DS_HR_EIGHTIETH_CHAR-1] != '8')) {
88                         return LDB_ERR_CONSTRAINT_VIOLATION;
89                 }
90                 if ((el->values[0].length >= DS_HR_SEVENTIETH_CHAR) &&
91                     (el->values[0].data[DS_HR_SEVENTIETH_CHAR-1] != '7')) {
92                         return LDB_ERR_CONSTRAINT_VIOLATION;
93                 }
94                 if ((el->values[0].length >= DS_HR_SIXTIETH_CHAR) &&
95                     (el->values[0].data[DS_HR_SIXTIETH_CHAR-1] != '6')) {
96                         return LDB_ERR_CONSTRAINT_VIOLATION;
97                 }
98                 if ((el->values[0].length >= DS_HR_FIFTIETH_CHAR) &&
99                     (el->values[0].data[DS_HR_FIFTIETH_CHAR-1] != '5')) {
100                         return LDB_ERR_CONSTRAINT_VIOLATION;
101                 }
102                 if ((el->values[0].length >= DS_HR_FOURTIETH_CHAR) &&
103                     (el->values[0].data[DS_HR_FOURTIETH_CHAR-1] != '4')) {
104                         return LDB_ERR_CONSTRAINT_VIOLATION;
105                 }
106                 if ((el->values[0].length >= DS_HR_THIRTIETH_CHAR) &&
107                     (el->values[0].data[DS_HR_THIRTIETH_CHAR-1] != '3')) {
108                         return LDB_ERR_CONSTRAINT_VIOLATION;
109                 }
110                 if ((el->values[0].length >= DS_HR_TWENTIETH_CHAR) &&
111                     (el->values[0].data[DS_HR_TWENTIETH_CHAR-1] != '2')) {
112                         return LDB_ERR_CONSTRAINT_VIOLATION;
113                 }
114                 if ((el->values[0].length >= DS_HR_TENTH_CHAR) &&
115                     (el->values[0].data[DS_HR_TENTH_CHAR-1] != '1')) {
116                         return LDB_ERR_CONSTRAINT_VIOLATION;
117                 }
118         }
119
120         return LDB_SUCCESS;
121 }
122
123 /*
124   auto normalise values on input
125  */
126 static int oc_auto_normalise(struct ldb_context *ldb, const struct dsdb_attribute *attr,
127                              struct ldb_message *msg, struct ldb_message_element *el)
128 {
129         int i;
130         bool values_copied = false;
131
132         for (i=0; i<el->num_values; i++) {
133                 struct ldb_val v;
134                 int ret;
135                 ret = attr->ldb_schema_attribute->syntax->canonicalise_fn(ldb, el->values, &el->values[i], &v);
136                 if (ret != LDB_SUCCESS) {
137                         return ret;
138                 }
139                 if (data_blob_cmp(&v, &el->values[i]) == 0) {
140                         /* no need to replace it */
141                         talloc_free(v.data);
142                         continue;
143                 }
144
145                 /* we need to copy the values array on the first change */
146                 if (!values_copied) {
147                         struct ldb_val *v2;
148                         v2 = talloc_array(msg->elements, struct ldb_val, el->num_values);
149                         if (v2 == NULL) {
150                                 return ldb_oom(ldb);
151                         }
152                         memcpy(v2, el->values, sizeof(struct ldb_val) * el->num_values);
153                         el->values = v2;
154                         values_copied = true;
155                 }
156
157                 el->values[i] = v;
158         }
159         return LDB_SUCCESS;
160 }
161
162 static int attr_handler(struct oc_context *ac)
163 {
164         struct ldb_context *ldb;
165         struct ldb_message *msg;
166         struct ldb_request *child_req;
167         const struct dsdb_attribute *attr;
168         unsigned int i;
169         int ret;
170         WERROR werr;
171         struct dsdb_syntax_ctx syntax_ctx;
172
173         ldb = ldb_module_get_ctx(ac->module);
174
175         if (ac->req->operation == LDB_ADD) {
176                 msg = ldb_msg_copy_shallow(ac, ac->req->op.add.message);
177         } else {
178                 msg = ldb_msg_copy_shallow(ac, ac->req->op.mod.message);
179         }
180         if (msg == NULL) {
181                 return ldb_oom(ldb);
182         }
183         ac->msg = msg;
184
185         /* initialize syntax checking context */
186         dsdb_syntax_ctx_init(&syntax_ctx, ldb, ac->schema);
187
188         /* Check if attributes exist in the schema, if the values match,
189          * if they're not operational and fix the names to the match the schema
190          * case */
191         for (i = 0; i < msg->num_elements; i++) {
192                 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
193                                                          msg->elements[i].name);
194                 if (attr == NULL) {
195                         if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
196                             ac->req->operation != LDB_ADD) {
197                                 /* we allow this for dbcheck to fix
198                                    broken attributes */
199                                 goto no_attribute;
200                         }
201                         ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' was not found in the schema!",
202                                                msg->elements[i].name,
203                                                ldb_dn_get_linearized(msg->dn));
204                         return LDB_ERR_NO_SUCH_ATTRIBUTE;
205                 }
206
207                 if ((attr->linkID & 1) == 1 &&
208                     !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) &&
209                     !ldb_request_get_control(ac->req, DSDB_CONTROL_DBCHECK)) {
210                         /* Odd is for the target.  Illegal to modify */
211                         ldb_asprintf_errstring(ldb, 
212                                                "objectclass_attrs: attribute '%s' on entry '%s' must not be modified directly, it is a linked attribute", 
213                                                msg->elements[i].name,
214                                                ldb_dn_get_linearized(msg->dn));
215                         return LDB_ERR_UNWILLING_TO_PERFORM;
216                 }
217                 
218                 if (!(msg->elements[i].flags & LDB_FLAG_INTERNAL_DISABLE_VALIDATION)) {
219                         werr = attr->syntax->validate_ldb(&syntax_ctx, attr,
220                                                           &msg->elements[i]);
221                         if (!W_ERROR_IS_OK(werr) &&
222                             !ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
223                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' contains at least one invalid value!",
224                                                        msg->elements[i].name,
225                                                        ldb_dn_get_linearized(msg->dn));
226                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
227                         }
228                 }
229
230                 if ((attr->systemFlags & DS_FLAG_ATTR_IS_CONSTRUCTED) != 0) {
231                         ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' is constructed!",
232                                                msg->elements[i].name,
233                                                ldb_dn_get_linearized(msg->dn));
234                         if (ac->req->operation == LDB_ADD) {
235                                 return LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE;
236                         } else {
237                                 return LDB_ERR_CONSTRAINT_VIOLATION;
238                         }
239                 }
240
241                 /* "dSHeuristics" syntax check */
242                 if (ldb_attr_cmp(attr->lDAPDisplayName, "dSHeuristics") == 0) {
243                         ret = oc_validate_dsheuristics(&(msg->elements[i]));
244                         if (ret != LDB_SUCCESS) {
245                                 return ret;
246                         }
247                 }
248
249                 /* auto normalise some attribute values */
250                 if (attr->syntax->auto_normalise) {
251                         ret = oc_auto_normalise(ldb, attr, msg, &msg->elements[i]);
252                         if (ret != LDB_SUCCESS) {
253                                 return ret;
254                         }
255                 }
256
257                 /* Substitute the attribute name to match in case */
258                 msg->elements[i].name = attr->lDAPDisplayName;
259         }
260
261 no_attribute:
262         if (ac->req->operation == LDB_ADD) {
263                 ret = ldb_build_add_req(&child_req, ldb, ac,
264                                         msg, ac->req->controls,
265                                         ac, oc_op_callback, ac->req);
266                 LDB_REQ_SET_LOCATION(child_req);
267         } else {
268                 ret = ldb_build_mod_req(&child_req, ldb, ac,
269                                         msg, ac->req->controls,
270                                         ac, oc_op_callback, ac->req);
271                 LDB_REQ_SET_LOCATION(child_req);
272         }
273         if (ret != LDB_SUCCESS) {
274                 return ret;
275         }
276
277         return ldb_next_request(ac->module, child_req);
278 }
279
280 /*
281   these are attributes which are left over from old ways of doing
282   things in ldb, and are harmless
283  */
284 static const char *harmless_attrs[] = { "parentGUID", NULL };
285
286 static int attr_handler2(struct oc_context *ac)
287 {
288         struct ldb_context *ldb;
289         struct ldb_message_element *oc_element;
290         struct ldb_message *msg;
291         const char **must_contain, **may_contain, **found_must_contain;
292         /* There exists a hardcoded delete-protected attributes list in AD */
293         const char *del_prot_attributes[] = { "nTSecurityDescriptor",
294                 "objectSid", "sAMAccountType", "sAMAccountName", "groupType",
295                 "primaryGroupID", "userAccountControl", "accountExpires",
296                 "badPasswordTime", "badPwdCount", "codePage", "countryCode",
297                 "lastLogoff", "lastLogon", "logonCount", "pwdLastSet", NULL },
298                 **l;
299         const struct dsdb_attribute *attr;
300         unsigned int i;
301         bool found;
302
303         ldb = ldb_module_get_ctx(ac->module);
304
305         if (ac->search_res == NULL) {
306                 return ldb_operr(ldb);
307         }
308
309         /* We rely here on the preceeding "objectclass" LDB module which did
310          * already fix up the objectclass list (inheritance, order...). */
311         oc_element = ldb_msg_find_element(ac->search_res->message,
312                                           "objectClass");
313         if (oc_element == NULL) {
314                 return ldb_operr(ldb);
315         }
316
317         /* LSA-specific object classes are not allowed to be created over LDAP,
318          * so we need to tell if this connection is internal (trusted) or not
319          * (untrusted).
320          *
321          * Hongwei Sun from Microsoft explains:
322          * The constraint in 3.1.1.5.2.2 MS-ADTS means that LSA objects cannot
323          * be added or modified through the LDAP interface, instead they can
324          * only be handled through LSA Policy API.  This is also explained in
325          * 7.1.6.9.7 MS-ADTS as follows:
326          * "Despite being replicated normally between peer DCs in a domain,
327          * the process of creating or manipulating TDOs is specifically
328          * restricted to the LSA Policy APIs, as detailed in [MS-LSAD] section
329          * 3.1.1.5. Unlike other objects in the DS, TDOs may not be created or
330          *  manipulated by client machines over the LDAPv3 transport."
331          */
332         if (ldb_req_is_untrusted(ac->req)) {
333                 for (i = 0; i < oc_element->num_values; i++) {
334                         if ((strcmp((char *)oc_element->values[i].data,
335                                     "secret") == 0) ||
336                             (strcmp((char *)oc_element->values[i].data,
337                                     "trustedDomain") == 0)) {
338                                 ldb_asprintf_errstring(ldb, "objectclass_attrs: LSA objectclasses (entry '%s') cannot be created or changed over LDAP!",
339                                                        ldb_dn_get_linearized(ac->search_res->message->dn));
340                                 return LDB_ERR_UNWILLING_TO_PERFORM;
341                         }
342                 }
343         }
344
345         must_contain = dsdb_full_attribute_list(ac, ac->schema, oc_element,
346                                                 DSDB_SCHEMA_ALL_MUST);
347         may_contain =  dsdb_full_attribute_list(ac, ac->schema, oc_element,
348                                                 DSDB_SCHEMA_ALL_MAY);
349         found_must_contain = const_str_list(str_list_copy(ac, must_contain));
350         if ((must_contain == NULL) || (may_contain == NULL)
351             || (found_must_contain == NULL)) {
352                 return ldb_operr(ldb);
353         }
354
355         /* Check the delete-protected attributes list */
356         msg = ac->search_res->message;
357         for (l = del_prot_attributes; *l != NULL; l++) {
358                 struct ldb_message_element *el;
359
360                 el = ldb_msg_find_element(ac->msg, *l);
361                 if (el == NULL) {
362                         /*
363                          * It was not specified in the add or modify,
364                          * so it doesn't need to be in the stored record
365                          */
366                         continue;
367                 }
368
369                 found = str_list_check_ci(must_contain, *l);
370                 if (!found) {
371                         found = str_list_check_ci(may_contain, *l);
372                 }
373                 if (found && (ldb_msg_find_element(msg, *l) == NULL)) {
374                         ldb_asprintf_errstring(ldb, "objectclass_attrs: delete protected attribute '%s' on entry '%s' missing!",
375                                                *l,
376                                                ldb_dn_get_linearized(msg->dn));
377                         return LDB_ERR_UNWILLING_TO_PERFORM;
378                 }
379         }
380
381         /* Check if all specified attributes are valid in the given
382          * objectclasses and if they meet additional schema restrictions. */
383         for (i = 0; i < msg->num_elements; i++) {
384                 attr = dsdb_attribute_by_lDAPDisplayName(ac->schema,
385                                                          msg->elements[i].name);
386                 if (attr == NULL) {
387                         if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID)) {
388                                 /* allow this to make it possible for dbcheck
389                                    to remove bad attributes */
390                                 continue;
391                         }
392                         return ldb_operr(ldb);
393                 }
394
395                 /* We can use "str_list_check" with "strcmp" here since the
396                  * attribute information from the schema are always equal
397                  * up-down-cased. */
398                 found = str_list_check(must_contain, attr->lDAPDisplayName);
399                 if (found) {
400                         str_list_remove(found_must_contain, attr->lDAPDisplayName);
401                 } else {
402                         found = str_list_check(may_contain, attr->lDAPDisplayName);
403                 }
404                 if (!found) {
405                         found = str_list_check(harmless_attrs, attr->lDAPDisplayName);
406                 }
407                 if (!found) {
408                         ldb_asprintf_errstring(ldb, "objectclass_attrs: attribute '%s' on entry '%s' does not exist in the specified objectclasses!",
409                                                msg->elements[i].name,
410                                                ldb_dn_get_linearized(msg->dn));
411                         return LDB_ERR_OBJECT_CLASS_VIOLATION;
412                 }
413         }
414
415         if (found_must_contain[0] != NULL &&
416             ldb_msg_check_string_attribute(msg, "isDeleted", "TRUE") == 0) {
417                 ldb_asprintf_errstring(ldb, "objectclass_attrs: at least one mandatory attribute ('%s') on entry '%s' wasn't specified!",
418                                        found_must_contain[0],
419                                        ldb_dn_get_linearized(msg->dn));
420                 return LDB_ERR_OBJECT_CLASS_VIOLATION;
421         }
422
423         return ldb_module_done(ac->req, ac->mod_ares->controls,
424                                ac->mod_ares->response, LDB_SUCCESS);
425 }
426
427 static int get_search_callback(struct ldb_request *req, struct ldb_reply *ares)
428 {
429         struct ldb_context *ldb;
430         struct oc_context *ac;
431         int ret;
432
433         ac = talloc_get_type(req->context, struct oc_context);
434         ldb = ldb_module_get_ctx(ac->module);
435
436         if (!ares) {
437                 return ldb_module_done(ac->req, NULL, NULL,
438                                        LDB_ERR_OPERATIONS_ERROR);
439         }
440         if (ares->error != LDB_SUCCESS) {
441                 return ldb_module_done(ac->req, ares->controls,
442                                        ares->response, ares->error);
443         }
444
445         ldb_reset_err_string(ldb);
446
447         switch (ares->type) {
448         case LDB_REPLY_ENTRY:
449                 if (ac->search_res != NULL) {
450                         ldb_set_errstring(ldb, "Too many results");
451                         talloc_free(ares);
452                         return ldb_module_done(ac->req, NULL, NULL,
453                                                LDB_ERR_OPERATIONS_ERROR);
454                 }
455
456                 ac->search_res = talloc_steal(ac, ares);
457                 break;
458
459         case LDB_REPLY_REFERRAL:
460                 /* ignore */
461                 talloc_free(ares);
462                 break;
463
464         case LDB_REPLY_DONE:
465                 talloc_free(ares);
466                 ret = attr_handler2(ac);
467                 if (ret != LDB_SUCCESS) {
468                         return ldb_module_done(ac->req, NULL, NULL, ret);
469                 }
470                 break;
471         }
472
473         return LDB_SUCCESS;
474 }
475
476 static int oc_op_callback(struct ldb_request *req, struct ldb_reply *ares)
477 {
478         struct oc_context *ac;
479         struct ldb_context *ldb;
480         struct ldb_request *search_req;
481         struct ldb_dn *base_dn;
482         int ret;
483
484         ac = talloc_get_type(req->context, struct oc_context);
485         ldb = ldb_module_get_ctx(ac->module);
486
487         if (!ares) {
488                 return ldb_module_done(ac->req, NULL, NULL,
489                                        LDB_ERR_OPERATIONS_ERROR);
490         }
491
492         if (ares->type == LDB_REPLY_REFERRAL) {
493                 return ldb_module_send_referral(ac->req, ares->referral);
494         }
495
496         if (ares->error != LDB_SUCCESS) {
497                 return ldb_module_done(ac->req, ares->controls, ares->response,
498                                        ares->error);
499         }
500
501         if (ares->type != LDB_REPLY_DONE) {
502                 talloc_free(ares);
503                 return ldb_module_done(ac->req, NULL, NULL,
504                                        LDB_ERR_OPERATIONS_ERROR);
505         }
506
507         ac->search_res = NULL;
508         ac->mod_ares = talloc_steal(ac, ares);
509
510         /* This looks up all attributes of our just added/modified entry */
511         base_dn = ac->req->operation == LDB_ADD ? ac->req->op.add.message->dn
512                 : ac->req->op.mod.message->dn;
513         ret = ldb_build_search_req(&search_req, ldb, ac, base_dn,
514                                    LDB_SCOPE_BASE, "(objectClass=*)",
515                                    NULL, NULL, ac,
516                                    get_search_callback, ac->req);
517         LDB_REQ_SET_LOCATION(search_req);
518         if (ret != LDB_SUCCESS) {
519                 return ldb_module_done(ac->req, NULL, NULL, ret);
520         }
521
522         ret = ldb_request_add_control(search_req, LDB_CONTROL_SHOW_RECYCLED_OID,
523                                       true, NULL);
524         if (ret != LDB_SUCCESS) {
525                 return ldb_module_done(ac->req, NULL, NULL, ret);
526         }
527
528         ret = ldb_next_request(ac->module, search_req);
529         if (ret != LDB_SUCCESS) {
530                 return ldb_module_done(ac->req, NULL, NULL, ret);
531         }
532
533         /* "ldb_module_done" isn't called here since we need to do additional
534          * checks. It is called at the end of "attr_handler2". */
535         return LDB_SUCCESS;
536 }
537
538 static int objectclass_attrs_add(struct ldb_module *module,
539                                  struct ldb_request *req)
540 {
541         struct ldb_context *ldb;
542         struct oc_context *ac;
543
544         ldb = ldb_module_get_ctx(module);
545
546         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_add\n");
547
548         /* do not manipulate our control entries */
549         if (ldb_dn_is_special(req->op.add.message->dn)) {
550                 return ldb_next_request(module, req);
551         }
552
553         ac = oc_init_context(module, req);
554         if (ac == NULL) {
555                 return ldb_operr(ldb);
556         }
557
558         /* without schema, there isn't much to do here */
559         if (ac->schema == NULL) {
560                 talloc_free(ac);
561                 return ldb_next_request(module, req);
562         }
563
564         return attr_handler(ac);
565 }
566
567 static int objectclass_attrs_modify(struct ldb_module *module,
568                                     struct ldb_request *req)
569 {
570         struct ldb_context *ldb;
571         struct oc_context *ac;
572
573         ldb = ldb_module_get_ctx(module);
574
575         ldb_debug(ldb, LDB_DEBUG_TRACE, "objectclass_attrs_modify\n");
576
577         /* do not manipulate our control entries */
578         if (ldb_dn_is_special(req->op.mod.message->dn)) {
579                 return ldb_next_request(module, req);
580         }
581
582         ac = oc_init_context(module, req);
583         if (ac == NULL) {
584                 return ldb_operr(ldb);
585         }
586
587         /* without schema, there isn't much to do here */
588         if (ac->schema == NULL) {
589                 talloc_free(ac);
590                 return ldb_next_request(module, req);
591         }
592
593         return attr_handler(ac);
594 }
595
596 static const struct ldb_module_ops ldb_objectclass_attrs_module_ops = {
597         .name              = "objectclass_attrs",
598         .add               = objectclass_attrs_add,
599         .modify            = objectclass_attrs_modify
600 };
601
602 int ldb_objectclass_attrs_module_init(const char *version)
603 {
604         LDB_MODULE_CHECK_VERSION(version);
605         return ldb_register_module(&ldb_objectclass_attrs_module_ops);
606 }